/*
 * 版权所有 (c) 2018-至今，Redis有限公司
 * 保留所有权利。
 *
 * 根据您的选择，在以下许可下使用：(a) Redis源代码可用许可 2.0
 * (RSALv2)；或 (b) 服务器端公共许可 v1 (SSPLv1)；或 (c) GNU Affero
 * 通用公共许可 v3 (AGPLv3)。
 */

#include "server.h"
#include "cluster.h"
#include "sha256.h"
#include <fcntl.h>
#include <ctype.h>

/* =============================================================================
 * ACL（访问控制列表）的全局状态
 * ==========================================================================*/

rax *Users; /* 映射用户名到用户结构的表 */

user *DefaultUser;  /* 默认用户的全局引用
                       如果没有使用AUTH或HELLO认证为其他用户，每个新连接都与此用户关联 */

list *UsersToLoad;  /* 这是在配置文件中找到的用户列表
                       需要在Redis初始化的最后阶段加载，此时所有模块都已加载完成
                       每个列表元素是一个以NULL结尾的SDS指针数组：第一个是用户名，
                       其余所有指针是与ACLSetUser()格式相同的ACL规则 */
list *ACLLog;       /* 安全日志，用户可以使用ACL LOG命令查看 */

long long ACLLogEntryCount = 0; /* 创建的ACL日志条目数量 */

static rax *commandId = NULL; /* 命令名称到ID的映射 */

static unsigned long nextid = 0; /* 下一个未分配的命令ID */

#define ACL_MAX_CATEGORIES 64 /* 命令类别的最大数量 */

struct ACLCategoryItem {
    char *name;    // 类别名称
    uint64_t flag; // 类别标志位
} ACLDefaultCommandCategories[] = { /* 有关每个类别的详细信息，请参见redis.conf配置文件 */
    {"keyspace", ACL_CATEGORY_KEYSPACE},
    {"read", ACL_CATEGORY_READ},
    {"write", ACL_CATEGORY_WRITE},
    {"set", ACL_CATEGORY_SET},
    {"sortedset", ACL_CATEGORY_SORTEDSET},
    {"list", ACL_CATEGORY_LIST},
    {"hash", ACL_CATEGORY_HASH},
    {"string", ACL_CATEGORY_STRING},
    {"bitmap", ACL_CATEGORY_BITMAP},
    {"hyperloglog", ACL_CATEGORY_HYPERLOGLOG},
    {"geo", ACL_CATEGORY_GEO},
    {"stream", ACL_CATEGORY_STREAM},
    {"pubsub", ACL_CATEGORY_PUBSUB},
    {"admin", ACL_CATEGORY_ADMIN},
    {"fast", ACL_CATEGORY_FAST},
    {"slow", ACL_CATEGORY_SLOW},
    {"blocking", ACL_CATEGORY_BLOCKING},
    {"dangerous", ACL_CATEGORY_DANGEROUS},
    {"connection", ACL_CATEGORY_CONNECTION},
    {"transaction", ACL_CATEGORY_TRANSACTION},
    {"scripting", ACL_CATEGORY_SCRIPTING},
    {NULL,0} /* Terminator. */
};

static struct ACLCategoryItem *ACLCommandCategories = NULL;
static size_t nextCommandCategory = 0; /* Index of the next command category to be added */

/* 实现运行时向ACL类别列表添加类别的功能。由于每个ACL类别都需要acl_categories标志中的一个位，
 * 因此可以添加的数量有限制。新的ACL类别占用acl_categories标志中除默认ACL命令类别占用的位之外的剩余位。
 * 
 * 可选的`flag`参数允许将`acl_categories`标志位分配给ACL类别。
 * 添加新类别时，除默认ACL命令类别外，此参数应为`0`，以允许函数将下一个可用的`acl_categories`标志位分配给新的ACL类别。
 *
 * 返回值：1表示添加成功，0表示失败（空间不足）
 *
 * 此函数存在于此是为了访问ACLCommandCategories数组并添加新的ACL类别。
 */
int ACLAddCommandCategory(const char *name, uint64_t flag) {
    if (nextCommandCategory >= ACL_MAX_CATEGORIES) return 0;
    ACLCommandCategories[nextCommandCategory].name = zstrdup(name);
    ACLCommandCategories[nextCommandCategory].flag = flag != 0 ? flag : (1ULL<<nextCommandCategory);
    nextCommandCategory++;
    return 1;
}

/* 使用默认ACL类别初始化ACLCommandCategories并为新的ACL类别分配空间 */
void ACLInitCommandCategories(void) {
    ACLCommandCategories = zcalloc(sizeof(struct ACLCategoryItem) * (ACL_MAX_CATEGORIES + 1));
    for (int j = 0; ACLDefaultCommandCategories[j].flag; j++) {
        serverAssert(ACLAddCommandCategory(ACLDefaultCommandCategories[j].name, ACLDefaultCommandCategories[j].flag));
    }
}

/* 此函数从`ACLCommandCategories`数组的末尾移除指定数量的类别。
 * 这样做的目的是移除在onload函数执行期间失败的模块所添加的类别。
 */
void ACLCleanupCategoriesOnFailure(size_t num_acl_categories_added) {
    for (size_t j = nextCommandCategory - num_acl_categories_added; j < nextCommandCategory; j++) {
        zfree(ACLCommandCategories[j].name);
        ACLCommandCategories[j].name = NULL;
        ACLCommandCategories[j].flag = 0;
    }
    nextCommandCategory -= num_acl_categories_added;
}

struct ACLUserFlag {
    const char *name; // 用户标志名称
    uint64_t flag;    // 对应的标志位值
} ACLUserFlags[] = {
    /* 注意：这里的顺序决定了ACLDescribeUser命令输出的顺序 */
    {"on", USER_FLAG_ENABLED},
    {"off", USER_FLAG_DISABLED},
    {"nopass", USER_FLAG_NOPASS},
    {"skip-sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD_SKIP},
    {"sanitize-payload", USER_FLAG_SANITIZE_PAYLOAD},
    {NULL,0} /* 终止符 */
};

struct ACLSelectorFlags {
    const char *name; // 选择器标志名称
    uint64_t flag;    // 对应的标志位值
} ACLSelectorFlags[] = {
    /* 注意：这里的顺序决定了ACLDescribeUser命令输出的顺序 */
    {"allkeys", SELECTOR_FLAG_ALLKEYS},
    {"allchannels", SELECTOR_FLAG_ALLCHANNELS},
    {"allcommands", SELECTOR_FLAG_ALLCOMMANDS},
    {NULL,0} /* 终止符 */
};

/* ACL选择器是私有的，不会在acl.c外部暴露 */
typedef struct {
    uint32_t flags; /* 参见SELECTOR_FLAG_*定义 */
    /* allowed_commands中的位被设置表示用户有权执行该命令
     *
     * 如果某个给定命令的位未被设置，但该命令有允许的首参数，
     * Redis还会检查allowed_firstargs以了解该命令是否可以执行。 */
    uint64_t allowed_commands[USER_COMMAND_BITS_COUNT/64];
    /* allowed_firstargs用于ACL规则阻止对命令的访问，除非提供特定的argv[1]参数
     *
     * 对于每个命令ID（对应于allowed_commands中设置的命令位），
     * 此数组指向一个SDS字符串数组（以NULL指针终止），
     * 包含该命令允许的所有首参数。当不使用首参数匹配时，
     * 该字段设置为NULL以避免分配USER_COMMAND_BITS_COUNT个指针。 */
    sds **allowed_firstargs;
    list *patterns;  /* 允许的键模式列表。如果此字段为NULL，
                        除非用户设置了ALLKEYS标志，否则用户不能在命令中提及任何键。 */
    list *channels;  /* 允许的Pub/Sub通道模式列表。如果此字段为NULL，
                        除非用户设置了ALLCHANNELS标志，否则用户不能在
                        `PUBLISH`或[P][UNSUBSCRIBE]命令中提及任何通道。 */
    sds command_rules; /* 有序类别和命令的字符串表示，
                        * 用于重新生成原始ACL字符串以供显示。 */
} aclSelector;

/* 重置选择器中特定命令的允许首参数列表 */
void ACLResetFirstArgsForCommand(aclSelector *selector, unsigned long id);
/* 重置选择器中所有命令的允许首参数列表 */
void ACLResetFirstArgs(aclSelector *selector);
/* 为选择器中的特定命令添加一个允许的首参数 */
void ACLAddAllowedFirstArg(aclSelector *selector, unsigned long id, const char *sub);
/* 释放ACL日志条目 */
void ACLFreeLogEntry(void *le);
/* 根据操作设置选择器的属性 */
int ACLSetSelector(aclSelector *selector, const char *op, size_t oplen);

/* 哈希密码的字符串表示长度 */
#define HASH_PASSWORD_LEN (SHA256_BLOCK_SIZE*2)

/* =============================================================================
 * ACL实现其他部分的辅助函数
 * ==========================================================================*/

/* 如果字符串相同则返回0，不同则返回非0值
 * 比较以防止攻击者仅通过监控函数执行时间
 * 来获取有关字符串性质的信息。注意：两个字符串必须长度相同。
 */
int time_independent_strcmp(char *a, char *b, int len) {
    int diff = 0;
    for (int j = 0; j < len; j++) {
        diff |= (a[j] ^ b[j]);
    }
    return diff; /* If zero strings are the same. */
}

/* 给定一个SDS字符串，返回其SHA256十六进制表示形式作为
 * 一个新的SDS字符串。 */
sds ACLHashPassword(unsigned char *cleartext, size_t len) {
    SHA256_CTX ctx;
    unsigned char hash[SHA256_BLOCK_SIZE];
    char hex[HASH_PASSWORD_LEN];
    char *cset = "0123456789abcdef";

    sha256_init(&ctx);
    sha256_update(&ctx,(unsigned char*)cleartext,len);
    sha256_final(&ctx,hash);

    for (int j = 0; j < SHA256_BLOCK_SIZE; j++) {
        hex[j*2] = cset[((hash[j]&0xF0)>>4)];
        hex[j*2+1] = cset[(hash[j]&0xF)];
    }
    return sdsnewlen(hex,HASH_PASSWORD_LEN);
}

/* 给定一个哈希值和哈希长度，如果它是有效的密码哈希则返回C_OK，否则返回C_ERR */
int ACLCheckPasswordHash(unsigned char *hash, int hashlen) {
    if (hashlen != HASH_PASSWORD_LEN) {
        return C_ERR;
    }

    /* 密码哈希只能包含表示十六进制值的字符，即数字和小写字母'a'到'f' */
    for(int i = 0; i < HASH_PASSWORD_LEN; i++) {
        char c = hash[i];
        if ((c < 'a' || c > 'f') && (c < '0' || c > '9')) {
            return C_ERR;
        }
    }
    return C_OK;
}

/* =============================================================================
 * 低级ACL API
 * ==========================================================================*/

/* 如果指定的字符串包含空格或空字符，则返回1
 * 我们对用户名和键模式执行此检查，以便更简单地重写ACL规则，
 * 在ACL列表中展示，并避免由于在转义存在的情况下解析规则而可能出现的细微安全漏洞
 * 如果字符串没有空格，函数返回0 */
int ACLStringHasSpaces(const char *s, size_t len) {
    for (size_t i = 0; i < len; i++) {
        if (isspace(s[i]) || s[i] == 0) return 1;
    }
    return 0;
}

/* 给定类别名称，该命令返回相应的标志，如果没有匹配则返回0 */
uint64_t ACLGetCommandCategoryFlagByName(const char *name) {
    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
        if (!strcasecmp(name,ACLCommandCategories[j].name)) {
            return ACLCommandCategories[j].flag;
        }
    }
    return 0; /* No match. */
}

/* 在用户定义列表中搜索用户的方法。该列表包含用户参数数组，
 * 我们只搜索第一个参数（用户名）以查找匹配项 */
int ACLListMatchLoadedUser(void *definition, void *user) {
    sds *user_definition = definition;
    return sdscmp(user_definition[0], user) == 0;
}

/* 用于用户密码列表的密码/模式比较方法，以便我们可以使用listSearchKey()搜索项目 */
int ACLListMatchSds(void *a, void *b) {
    return sdscmp(a,b) == 0;
}

/* 从ACL用户密码/模式列表中释放列表元素的方法 */
void ACLListFreeSds(void *item) {
    sdsfreegeneric(item);
}

/* 复制ACL用户密码/模式列表中的列表元素的方法 */
void *ACLListDupSds(void *item) {
    return sdsdup(item);
}

/* 用于处理具有不同基于键的权限的键模式的结构 */
typedef struct {
    int flags; /* 此键模式的ACL键权限类型 */
    sds pattern; /* 用于匹配键的模式 */
} keyPattern;

/* 创建一个新的键模式 */
keyPattern *ACLKeyPatternCreate(sds pattern, int flags) {
    keyPattern *new = (keyPattern *) zmalloc(sizeof(keyPattern));
    new->pattern = pattern;
    new->flags = flags;
    return new;
}

/* 释放键模式及其内部结构 */
void ACLKeyPatternFree(keyPattern *pattern) {
    sdsfree(pattern->pattern);
    zfree(pattern);
}

/* 用于用户密码列表的密码/模式比较方法，以便我们可以使用listSearchKey()搜索项目 */
int ACLListMatchKeyPattern(void *a, void *b) {
    return sdscmp(((keyPattern *) a)->pattern,((keyPattern *) b)->pattern) == 0;
}

/* 从ACL用户密码/模式列表中释放列表元素的方法 */
void ACLListFreeKeyPattern(void *item) {
    ACLKeyPatternFree(item);
}

/* 复制ACL用户密码/模式列表中的列表元素的方法 */
void *ACLListDupKeyPattern(void *item) {
    keyPattern *old = (keyPattern *) item;
    return ACLKeyPatternCreate(sdsdup(old->pattern), old->flags);
}

/* 将键模式的字符串表示附加到提供的基础字符串上 */
sds sdsCatPatternString(sds base, keyPattern *pat) {
    if (pat->flags == ACL_ALL_PERMISSION) {
        base = sdscatlen(base,"~",1);
    } else if (pat->flags == ACL_READ_PERMISSION) {
        base = sdscatlen(base,"%R~",3);
    } else if (pat->flags == ACL_WRITE_PERMISSION) {
        base = sdscatlen(base,"%W~",3);
    } else {
        serverPanic("Invalid key pattern flag detected");
    }
    return sdscatsds(base, pat->pattern);
}

/* 使用提供的初始标志集创建一个空选择器。默认情况下，该选择器没有任何权限 */
aclSelector *ACLCreateSelector(int flags) {
    aclSelector *selector = zmalloc(sizeof(aclSelector));
    selector->flags = flags | server.acl_pubsub_default;
    selector->patterns = listCreate();
    selector->channels = listCreate();
    selector->allowed_firstargs = NULL;
    selector->command_rules = sdsempty();

    listSetMatchMethod(selector->patterns,ACLListMatchKeyPattern);
    listSetFreeMethod(selector->patterns,ACLListFreeKeyPattern);
    listSetDupMethod(selector->patterns,ACLListDupKeyPattern);
    listSetMatchMethod(selector->channels,ACLListMatchSds);
    listSetFreeMethod(selector->channels,ACLListFreeSds);
    listSetDupMethod(selector->channels,ACLListDupSds);
    memset(selector->allowed_commands,0,sizeof(selector->allowed_commands));

    return selector;
}

/* 清理提供的选择器，包括所有内部结构 */
void ACLFreeSelector(aclSelector *selector) {
    listRelease(selector->patterns);
    listRelease(selector->channels);
    sdsfree(selector->command_rules);
    ACLResetFirstArgs(selector);
    zfree(selector);
}

/* 创建提供的选择器的精确副本 */
aclSelector *ACLCopySelector(aclSelector *src) {
    aclSelector *dst = zmalloc(sizeof(aclSelector));
    dst->flags = src->flags;
    dst->patterns = listDup(src->patterns);
    dst->channels = listDup(src->channels);
    dst->command_rules = sdsdup(src->command_rules);
    memcpy(dst->allowed_commands,src->allowed_commands,
           sizeof(dst->allowed_commands));
    dst->allowed_firstargs = NULL;
    /* Copy the allowed first-args array of array of SDS strings. */
    if (src->allowed_firstargs) {
        for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {
            if (!(src->allowed_firstargs[j])) continue;
            for (int i = 0; src->allowed_firstargs[j][i]; i++) {
                ACLAddAllowedFirstArg(dst, j, src->allowed_firstargs[j][i]);
            }
        }
    }
    return dst;
}

/* 用于释放选择器的列表方法 */
void ACLListFreeSelector(void *a) {
    ACLFreeSelector((aclSelector *) a);
}

/* 用于复制选择器的列表方法 */
void *ACLListDuplicateSelector(void *src) {
    return ACLCopySelector((aclSelector *)src);
}

/* 所有用户都有一个隐式根选择器，
 * 它为旧的ACL权限提供向后兼容性 */
aclSelector *ACLUserGetRootSelector(user *u) {
    serverAssert(listLength(u->selectors));
    aclSelector *s = (aclSelector *) listNodeValue(listFirst(u->selectors));
    serverAssert(s->flags & SELECTOR_FLAG_ROOT);
    return s;
}

/* 使用指定的名称创建一个新用户，将其存储在用户列表中（Users全局基数树），
 * 并返回表示该用户的结构的引用
 *
 * 如果具有该名称的用户已存在，则返回NULL */
user *ACLCreateUser(const char *name, size_t namelen) {
    if (raxFind(Users,(unsigned char*)name,namelen,NULL)) return NULL;
    user *u = zmalloc(sizeof(*u));
    u->name = sdsnewlen(name,namelen);
    u->flags = USER_FLAG_DISABLED;
    u->flags |= USER_FLAG_SANITIZE_PAYLOAD;
    u->passwords = listCreate();
    u->acl_string = NULL;
    listSetMatchMethod(u->passwords,ACLListMatchSds);
    listSetFreeMethod(u->passwords,ACLListFreeSds);
    listSetDupMethod(u->passwords,ACLListDupSds);

    u->selectors = listCreate();
    listSetFreeMethod(u->selectors,ACLListFreeSelector);
    listSetDupMethod(u->selectors,ACLListDuplicateSelector);

    /* 添加初始根选择器 */
    aclSelector *s = ACLCreateSelector(SELECTOR_FLAG_ROOT);
    listAddNodeHead(u->selectors, s);

    raxInsert(Users,(unsigned char*)name,namelen,u,NULL);
    return u;
}

/* 当我们需要一个未链接的"假"用户时应调用此函数，
 * 可用于验证ACL规则或其他类似目的
 * 该用户不会链接到Users基数树。返回的用户应像通常一样使用ACLFreeUser()释放 */
user *ACLCreateUnlinkedUser(void) {
    char username[64];
    for (int j = 0; ; j++) {
        snprintf(username,sizeof(username),"__fakeuser:%d__",j);
        user *fakeuser = ACLCreateUser(username,strlen(username));
        if (fakeuser == NULL) continue;
        int retval = raxRemove(Users,(unsigned char*) username,
                               strlen(username),NULL);
        serverAssert(retval != 0);
        return fakeuser;
    }
}

/* 释放用户结构使用的内存。注意，此函数不会从Users全局基数树中删除用户 */
void ACLFreeUser(user *u) {
    sdsfree(u->name);
    if (u->acl_string) {
        decrRefCount(u->acl_string);
        u->acl_string = NULL;
    }
    listRelease(u->passwords);
    listRelease(u->selectors);
    zfree(u);
}

/* ACLFreeUser的通用版本 */
void ACLFreeUserGeneric(void *u) {
    ACLFreeUser((user *)u);
}

/* 当用户被删除时，我们需要循环处理活动连接，
 * 以终止所有使用该用户身份验证的待处理连接 */
void ACLFreeUserAndKillClients(user *u) {
    listIter li;
    listNode *ln;
    listRewind(server.clients,&li);
    while ((ln = listNext(&li)) != NULL) {
        client *c = listNodeValue(ln);
        if (c->user == u) {
            /* 我们将异步释放连接，因此理论上不需要设置不同的用户。
             * 但是，如果Redis中存在bug，迟早可能会导致安全漏洞：
             * 将其设置为默认用户并置于未认证模式更为安全。 */
            deauthenticateAndCloseClient(c);
        }
    }
    ACLFreeUser(u);
}

/* 将用户ACL规则从源用户'src'复制到目标用户'dst'，
 * 以便在过程结束时它们具有完全相同的规则（但名称将保持原始名称） */
void ACLCopyUser(user *dst, user *src) {
    listRelease(dst->passwords);
    listRelease(dst->selectors);
    dst->passwords = listDup(src->passwords);
    dst->selectors = listDup(src->selectors);
    dst->flags = src->flags;
    if (dst->acl_string) {
        decrRefCount(dst->acl_string);
    }
    dst->acl_string = src->acl_string;
    if (dst->acl_string) {
        /* 如果src为NULL，我们将其设置为NULL，否则需要增加引用计数 */
        incrRefCount(dst->acl_string);
    }
}

/* 给定一个命令ID，此函数通过引用设置'word'和'bit'，
 * 以便user->allowed_commands[word]将寻址到存储提供的ID对应位的正确字，
 * 并且user->allowed_commands[word]&bit将标识该特定位
 * 如果指定的ID溢出用户表示中的位图，函数返回C_ERR */
int ACLGetCommandBitCoordinates(uint64_t id, uint64_t *word, uint64_t *bit) {
    if (id >= USER_COMMAND_BITS_COUNT) return C_ERR;
    *word = id / sizeof(uint64_t) / 8;
    *bit = 1ULL << (id % (sizeof(uint64_t) * 8));
    return C_OK;
}

/* 检查指定用户是否设置了指定的命令位
 * 如果该位被设置，函数返回1，否则返回0
 * 注意，此函数不检查用户的ALLCOMMANDS标志，只检查底层位掩码
 *
 * 如果该位溢出用户的内部表示，则返回零，以便在这种边缘情况下不允许执行命令 */
int ACLGetSelectorCommandBit(const aclSelector *selector, unsigned long id) {
    uint64_t word, bit;
    if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return 0;
    return (selector->allowed_commands[word] & bit) != 0;
}

/* 当指定+@all或allcommands时，我们还设置一个保留位，
 * 以便以后测试用户是否有权执行"未来命令"，
 * 即通过模块稍后加载的命令 */
int ACLSelectorCanExecuteFutureCommands(aclSelector *selector) {
    return ACLGetSelectorCommandBit(selector,USER_COMMAND_BITS_COUNT-1);
}

/* 将指定用户的指定命令位设置为'value'（0或1）
 * 如果该位溢出用户的内部表示，则不执行任何操作
 * 调用此函数并设置值为零时，用户标志ALLCOMMANDS会被清除，
 * 因为不再可能跳过命令位的显式测试 */
void ACLSetSelectorCommandBit(aclSelector *selector, unsigned long id, int value) {
    uint64_t word, bit;
    if (ACLGetCommandBitCoordinates(id,&word,&bit) == C_ERR) return;
    if (value) {
        selector->allowed_commands[word] |= bit;
    } else {
        selector->allowed_commands[word] &= ~bit;
        selector->flags &= ~SELECTOR_FLAG_ALLCOMMANDS;
    }
}

/* 从保留的命令规则中删除规则。始终逐字匹配规则，
 * 但如果我们添加或删除整个命令，也会删除子命令规则 */
void ACLSelectorRemoveCommandRule(aclSelector *selector, sds new_rule) {
    size_t new_len = sdslen(new_rule);
    char *existing_rule = selector->command_rules;

    /* 遍历现有规则，尝试找到与新规则"匹配"的规则。如果找到匹配项，
     * 则通过复制后续规则来替换它，从而从字符串中删除该命令。 */
    while(existing_rule[0]) {
        /* The first character of the rule is +/-, which we don't need to compare. */
        char *copy_position = existing_rule;
        existing_rule += 1;

        /* Assume a trailing space after a command is part of the command, like '+get ', so trim it
         * as well if the command is removed. */
        char *rule_end = strchr(existing_rule, ' ');
        if (!rule_end) {
            /* This is the last rule, so move it to the end of the string. */
            rule_end = existing_rule + strlen(existing_rule);

            /* This approach can leave a trailing space if the last rule is removed,
             * but only if it's not the first rule, so handle that case. */
            if (copy_position != selector->command_rules) copy_position -= 1;
        }
        char *copy_end = rule_end;
        if (*copy_end == ' ') copy_end++;

        /* Exact match or the rule we are comparing is a subcommand denoted by '|' */
        size_t existing_len = rule_end - existing_rule;
        if (!memcmp(existing_rule, new_rule, min(existing_len, new_len))) {
            if ((existing_len == new_len) || (existing_len > new_len && (existing_rule[new_len]) == '|')) {
                /* Copy the remaining rules starting at the next rule to replace the rule to be
                 * deleted, including the terminating NULL character. */
                memmove(copy_position, copy_end, strlen(copy_end) + 1);
                existing_rule = copy_position;
                continue;
            }
        }
        existing_rule = copy_end;
    }

    /* There is now extra padding at the end of the rules, so clean that up. */
    sdsupdatelen(selector->command_rules);
}

/* 此函数负责更新command_rules结构，以便保持命令和类别的相对顺序，
 * 并且可以无损失地重现 */
void ACLUpdateCommandRules(aclSelector *selector, const char *rule, int allow) {
    sds new_rule = sdsnew(rule);
    sdstolower(new_rule);

    ACLSelectorRemoveCommandRule(selector, new_rule);
    if (sdslen(selector->command_rules)) selector->command_rules = sdscat(selector->command_rules, " ");
    selector->command_rules = sdscatfmt(selector->command_rules, allow ? "+%S" : "-%S", new_rule);
    sdsfree(new_rule);
}

/* 此函数用于允许/阻止特定命令。
 * 允许/阻止容器命令也适用于其子命令 */
void ACLChangeSelectorPerm(aclSelector *selector, struct redisCommand *cmd, int allow) {
    unsigned long id = cmd->id;
    ACLSetSelectorCommandBit(selector,id,allow);
    ACLResetFirstArgsForCommand(selector,id);
    if (cmd->subcommands_dict) {
        dictEntry *de;
        dictIterator *di = dictGetSafeIterator(cmd->subcommands_dict);
        while((de = dictNext(di)) != NULL) {
            struct redisCommand *sub = (struct redisCommand *)dictGetVal(de);
            ACLSetSelectorCommandBit(selector,sub->id,allow);
        }
        dictReleaseIterator(di);
    }
}

/* 这类似于ACLSetSelectorCommandBit()，但不是设置指定的ID，
 * 它会检查参数中指定的类别中的所有命令，
 * 并将对应于这些命令的所有位设置为指定的值
 * 由于用户传递的类别可能不存在，
 * 如果未找到类别，函数返回C_ERR；如果找到并执行了操作，则返回C_OK */
void ACLSetSelectorCommandBitsForCategory(dict *commands, aclSelector *selector, uint64_t cflag, int value) {
    dictIterator *di = dictGetIterator(commands);
    dictEntry *de;
    while ((de = dictNext(di)) != NULL) {
        struct redisCommand *cmd = dictGetVal(de);
        if (cmd->acl_categories & cflag) {
            ACLChangeSelectorPerm(selector,cmd,value);
        }
        if (cmd->subcommands_dict) {
            ACLSetSelectorCommandBitsForCategory(cmd->subcommands_dict, selector, cflag, value);
        }
    }
    dictReleaseIterator(di);
}

/* 此函数负责为现有用户的所有选择器重计算命令位
 * 它使用'command_rules'（有序类别和命令的字符串表示）
 * 来重新计算命令位 */
void ACLRecomputeCommandBitsFromCommandRulesAllUsers(void) {
    raxIterator ri;
    raxStart(&ri,Users);
    raxSeek(&ri,"^",NULL,0);
    while(raxNext(&ri)) {
        user *u = ri.data;
        listIter li;
        listNode *ln;
        listRewind(u->selectors,&li);
        while((ln = listNext(&li))) {
            aclSelector *selector = (aclSelector *) listNodeValue(ln);
            int argc = 0;
            sds *argv = sdssplitargs(selector->command_rules, &argc);
            serverAssert(argv != NULL);
            /* Checking selector's permissions for all commands to start with a clean state. */
            if (ACLSelectorCanExecuteFutureCommands(selector)) {
                int res = ACLSetSelector(selector,"+@all",-1);
                serverAssert(res == C_OK);
            } else {
                int res = ACLSetSelector(selector,"-@all",-1);
                serverAssert(res == C_OK);
            }

            /* Apply all of the commands and categories to this selector. */
            for(int i = 0; i < argc; i++) {
                int res = ACLSetSelector(selector, argv[i], sdslen(argv[i]));
                serverAssert(res == C_OK);
            }
            sdsfreesplitres(argv, argc);
        }
    }
    raxStop(&ri);

}

int ACLSetSelectorCategory(aclSelector *selector, const char *category, int allow) {
    uint64_t cflag = ACLGetCommandCategoryFlagByName(category + 1);
    if (!cflag) return C_ERR;

    ACLUpdateCommandRules(selector, category, allow);

    /* Set the actual command bits on the selector. */
    ACLSetSelectorCommandBitsForCategory(server.orig_commands, selector, cflag, allow);
    return C_OK;
}

void ACLCountCategoryBitsForCommands(dict *commands, aclSelector *selector, unsigned long *on, unsigned long *off, uint64_t cflag) {
    dictIterator *di = dictGetIterator(commands);
    dictEntry *de;
    while ((de = dictNext(di)) != NULL) {
        struct redisCommand *cmd = dictGetVal(de);
        if (cmd->acl_categories & cflag) {
            if (ACLGetSelectorCommandBit(selector,cmd->id))
                (*on)++;
            else
                (*off)++;
        }
        if (cmd->subcommands_dict) {
            ACLCountCategoryBitsForCommands(cmd->subcommands_dict, selector, on, off, cflag);
        }
    }
    dictReleaseIterator(di);
}

/* 返回用户'u'在指定类别名称标记的命令子集中允许（开）和拒绝（关）的命令数量
 * 如果类别名称无效，返回C_ERR；否则返回C_OK，并通过引用填充on和off */
int ACLCountCategoryBitsForSelector(aclSelector *selector, unsigned long *on, unsigned long *off,
                                const char *category)
{
    uint64_t cflag = ACLGetCommandCategoryFlagByName(category);
    if (!cflag) return C_ERR;

    *on = *off = 0;
    ACLCountCategoryBitsForCommands(server.orig_commands, selector, on, off, cflag);
    return C_OK;
}

/* 此函数返回一个SDS字符串，表示与命令执行相关的指定选择器ACL规则，
 * 格式与您可以使用ACL SETUSER设置它们的格式相同
 * 该函数仅返回重新创建用户命令位图所需的规则集，
 * 不包括其他用户标志，如on/off、密码等
 * 返回的字符串始终以+@all或-@all规则开头，具体取决于用户位图，
 * 必要时后跟缩小或扩展用户可执行操作所需的其他规则 */
sds ACLDescribeSelectorCommandRules(aclSelector *selector) {
    sds rules = sdsempty();

    /* 我们使用这个假选择器作为"合理性"检查，确保我们生成的规则
     * 与当前选择器上的位图相同。 */
    aclSelector *fake_selector = ACLCreateSelector(0);

    /* 在这里我们想确定应该从+@all还是-@all开始。
     * 请注意，当从+@all开始并减去权限时，用户将能够执行未来添加的命令，
     * 而从-@all开始并添加权限只会允许用户运行选定的命令和/或类别。
     * 我们如何测试这一点？我们使用一个保留的命令ID位的技巧，
     * 该位只能通过+@all（及其别名"allcommands"）设置。 */
    if (ACLSelectorCanExecuteFutureCommands(selector)) {
        rules = sdscat(rules,"+@all ");
        ACLSetSelector(fake_selector,"+@all",-1);
    } else {
        rules = sdscat(rules,"-@all ");
        ACLSetSelector(fake_selector,"-@all",-1);
    }

    /* Apply all of the commands and categories to the fake selector. */
    int argc = 0;
    sds *argv = sdssplitargs(selector->command_rules, &argc);
    serverAssert(argv != NULL);

    for(int i = 0; i < argc; i++) {
        int res = ACLSetSelector(fake_selector, argv[i], -1);
        serverAssert(res == C_OK);
    }
    if (sdslen(selector->command_rules)) {
        rules = sdscatfmt(rules, "%S ", selector->command_rules);
    }
    sdsfreesplitres(argv, argc);

    /* Trim the final useless space. */
    sdsrange(rules,0,-2);

    /* 从技术上讲这不是必需的，但我们希望验证预测的位图现在与用户位图完全相同，
     * 否则中止，因为在此代码路径中，中止比安全风险更好。 */
    if (memcmp(fake_selector->allowed_commands,
                        selector->allowed_commands,
                        sizeof(selector->allowed_commands)) != 0)
    {
        serverLog(LL_WARNING,
            "CRITICAL ERROR: User ACLs don't match final bitmap: '%s'",
            rules);
        serverPanic("No bitmap match in ACLDescribeSelectorCommandRules()");
    }
    ACLFreeSelector(fake_selector);
    return rules;
}

sds ACLDescribeSelector(aclSelector *selector) {
    listIter li;
    listNode *ln;
    sds res = sdsempty();
    /* Key patterns. */
    if (selector->flags & SELECTOR_FLAG_ALLKEYS) {
        res = sdscatlen(res,"~* ",3);
    } else {
        listRewind(selector->patterns,&li);
        while((ln = listNext(&li))) {
            keyPattern *thispat = (keyPattern *)listNodeValue(ln);
            res = sdsCatPatternString(res, thispat);
            res = sdscatlen(res," ",1);
        }
    }

    /* Pub/sub channel patterns. */
    if (selector->flags & SELECTOR_FLAG_ALLCHANNELS) {
        res = sdscatlen(res,"&* ",3);
    } else {
        res = sdscatlen(res,"resetchannels ",14);
        listRewind(selector->channels,&li);
        while((ln = listNext(&li))) {
            sds thispat = listNodeValue(ln);
            res = sdscatlen(res,"&",1);
            res = sdscatsds(res,thispat);
            res = sdscatlen(res," ",1);
        }
    }

    /* Command rules. */
    sds rules = ACLDescribeSelectorCommandRules(selector);
    res = sdscatsds(res,rules);
    sdsfree(rules);
    return res;
}

/* 这类似于ACLDescribeSelectorCommandRules()，但它不仅描述用户命令规则，
 * 还描述所有内容：用户标志、键、密码，最后是通过ACLDescribeSelectorCommandRules()
 * 函数获得的命令规则
 * 这是我们在想要重写描述ACL的配置文件以及显示ACL LIST用户时调用的函数 */
robj *ACLDescribeUser(user *u) {
    if (u->acl_string) {
        incrRefCount(u->acl_string);
        return u->acl_string;
    }

    sds res = sdsempty();

    /* Flags. */
    for (int j = 0; ACLUserFlags[j].flag; j++) {
        if (u->flags & ACLUserFlags[j].flag) {
            res = sdscat(res,ACLUserFlags[j].name);
            res = sdscatlen(res," ",1);
        }
    }

    /* Passwords. */
    listIter li;
    listNode *ln;
    listRewind(u->passwords,&li);
    while((ln = listNext(&li))) {
        sds thispass = listNodeValue(ln);
        res = sdscatlen(res,"#",1);
        res = sdscatsds(res,thispass);
        res = sdscatlen(res," ",1);
    }

    /* Selectors (Commands and keys) */
    listRewind(u->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *selector = (aclSelector *) listNodeValue(ln);
        sds default_perm = ACLDescribeSelector(selector);
        if (selector->flags & SELECTOR_FLAG_ROOT) {
            res = sdscatfmt(res, "%s", default_perm);
        } else {
            res = sdscatfmt(res, " (%s)", default_perm);
        }
        sdsfree(default_perm);
    }

    u->acl_string = createObject(OBJ_STRING, res);
    /* 因为我们要返回它，所以必须增加引用计数 */
    incrRefCount(u->acl_string);

    return u->acl_string;
}

/* 从原始命令表中获取命令，该命令不受命令重命名操作的影响：
 * 我们所有的ACL工作都基于该表，这样无论命令如何重命名，ACL都是有效的 */
struct redisCommand *ACLLookupCommand(const char *name) {
    struct redisCommand *cmd;
    sds sdsname = sdsnew(name);
    cmd = lookupCommandBySdsLogic(server.orig_commands,sdsname);
    sdsfree(sdsname);
    return cmd;
}

/* 清除指定用户和命令ID的允许第一个参数数组 */
void ACLResetFirstArgsForCommand(aclSelector *selector, unsigned long id) {
    if (selector->allowed_firstargs && selector->allowed_firstargs[id]) {
        for (int i = 0; selector->allowed_firstargs[id][i]; i++)
            sdsfree(selector->allowed_firstargs[id][i]);
        zfree(selector->allowed_firstargs[id]);
        selector->allowed_firstargs[id] = NULL;
    }
}

/* 清除整个first-args表。这在+@all、-@all等情况下很有用，
 * 可以返回用户的最小内存使用量（和要执行的检查） */
void ACLResetFirstArgs(aclSelector *selector) {
    if (selector->allowed_firstargs == NULL) return;
    for (int j = 0; j < USER_COMMAND_BITS_COUNT; j++) {
        if (selector->allowed_firstargs[j]) {
            for (int i = 0; selector->allowed_firstargs[j][i]; i++)
                sdsfree(selector->allowed_firstargs[j][i]);
            zfree(selector->allowed_firstargs[j]);
        }
    }
    zfree(selector->allowed_firstargs);
    selector->allowed_firstargs = NULL;
}

/* 向用户'u'的子命令列表和指定的命令ID添加一个first-arg */
void ACLAddAllowedFirstArg(aclSelector *selector, unsigned long id, const char *sub) {
    /* 如果这是要为此用户配置的第一个first-arg，
     * 我们必须分配first-args数组。 */
    if (selector->allowed_firstargs == NULL) {
        selector->allowed_firstargs = zcalloc(USER_COMMAND_BITS_COUNT * sizeof(sds*));
    }

    /* 我们还需要扩大指向以NULL结尾的SDS数组的分配，
     * 为这个元素腾出空间。首先检查当前大小，同时确保
     * first-arg尚未在其中指定。 */
    long items = 0;
    if (selector->allowed_firstargs[id]) {
        while(selector->allowed_firstargs[id][items]) {
            /* 如果它已经在这里，不要再添加一次。 */
            if (!strcasecmp(selector->allowed_firstargs[id][items],sub))
                return;
            items++;
        }
    }

    /* 现在我们可以为新项目（和空终止符）腾出空间。 */
    items += 2;
    selector->allowed_firstargs[id] = zrealloc(selector->allowed_firstargs[id], sizeof(sds)*items);
    selector->allowed_firstargs[id][items-2] = sdsnew(sub);
    selector->allowed_firstargs[id][items-1] = NULL;
}

/* 从给定的ACL操作创建ACL选择器，这些操作应该是以括号开始和结束的
 * 以空格分隔的ACL操作列表
 *
 * 如果任何操作无效，则返回NULL，并设置与内部错误对应的errno */
aclSelector *aclCreateSelectorFromOpSet(const char *opset, size_t opsetlen) {
    serverAssert(opset[0] == '(' && opset[opsetlen - 1] == ')');
    aclSelector *s = ACLCreateSelector(0);

    int argc = 0;
    sds trimmed = sdsnewlen(opset + 1, opsetlen - 2);
    sds *argv = sdssplitargs(trimmed, &argc);
    for (int i = 0; i < argc; i++) {
        if (ACLSetSelector(s, argv[i], sdslen(argv[i])) == C_ERR) {
            ACLFreeSelector(s);
            s = NULL;
            goto cleanup;
        }
    }

cleanup:
    sdsfreesplitres(argv, argc);
    sdsfree(trimmed);
    return s;
}

/* 使用提供的'op'设置选择器的属性
 *
 * +<command>   允许执行该命令
 *              可与`|`一起使用以允许子命令（例如 "+config|get"）
 * -<command>   禁止执行该命令
 *              可与`|`一起使用以阻止子命令（例如 "-config|set"）
 * +@<category> 允许执行该类别中的所有命令
 *              有效的类别如 @admin、@set、@sortedset 等
 *              完整列表请参见server.c文件中Redis命令表的描述和定义
 *              特殊类别@all表示所有当前存在于服务器中以及将来通过模块加载的命令
 * +<command>|first-arg    允许一个已禁用命令的特定第一个参数
 *                         注意，这种形式不允许作为负数，如 -SELECT|1，
 *                         只能以"+"开头的加法形式使用.
 * allcommands  +@all的别名。注意，它意味着能够执行通过模块系统加载的所有未来命令
 * nocommands   -@all的别名
 * ~<pattern>   添加可以作为命令一部分提及的键模式
 *              例如~*允许所有键。该模式是类似于KEYS的glob风格模式
 *              可以指定多个模式
 * %R~<pattern> 添加键读取模式，指定可以读取哪些键
 * %W~<pattern> 添加键写入模式，指定可以写入哪些键
 * allkeys      ~*的别名
 * resetkeys    清除允许的键模式列表
 * &<pattern>   添加可以作为Pub/Sub命令一部分提及的通道模式
 *              例如&*允许所有通道。该模式是类似于PSUBSCRIBE的glob风格模式
 *              可以指定多个模式
 * allchannels  &*的别名
 * resetchannels  清除允许的通道模式列表
 */
int ACLSetSelector(aclSelector *selector, const char* op, size_t oplen) {
    if (!strcasecmp(op,"allkeys") ||
               !strcasecmp(op,"~*"))
    {
        selector->flags |= SELECTOR_FLAG_ALLKEYS;
        listEmpty(selector->patterns);
    } else if (!strcasecmp(op,"resetkeys")) {
        selector->flags &= ~SELECTOR_FLAG_ALLKEYS;
        listEmpty(selector->patterns);
    } else if (!strcasecmp(op,"allchannels") ||
               !strcasecmp(op,"&*"))
    {
        selector->flags |= SELECTOR_FLAG_ALLCHANNELS;
        listEmpty(selector->channels);
    } else if (!strcasecmp(op,"resetchannels")) {
        selector->flags &= ~SELECTOR_FLAG_ALLCHANNELS;
        listEmpty(selector->channels);
    } else if (!strcasecmp(op,"allcommands") ||
               !strcasecmp(op,"+@all"))
    {
        memset(selector->allowed_commands,255,sizeof(selector->allowed_commands));
        selector->flags |= SELECTOR_FLAG_ALLCOMMANDS;
        sdsclear(selector->command_rules);
        ACLResetFirstArgs(selector);
    } else if (!strcasecmp(op,"nocommands") ||
               !strcasecmp(op,"-@all"))
    {
        memset(selector->allowed_commands,0,sizeof(selector->allowed_commands));
        selector->flags &= ~SELECTOR_FLAG_ALLCOMMANDS;
        sdsclear(selector->command_rules);
        ACLResetFirstArgs(selector);
    } else if (op[0] == '~' || op[0] == '%') {
        if (selector->flags & SELECTOR_FLAG_ALLKEYS) {
            errno = EEXIST;
            return C_ERR;
        }
        int flags = 0;
        size_t offset = 1;
        if (op[0] == '%') {
            int perm_ok = 1;
            for (; offset < oplen; offset++) {
                if (toupper(op[offset]) == 'R' && !(flags & ACL_READ_PERMISSION)) {
                    flags |= ACL_READ_PERMISSION;
                } else if (toupper(op[offset]) == 'W' && !(flags & ACL_WRITE_PERMISSION)) {
                    flags |= ACL_WRITE_PERMISSION;
                } else if (op[offset] == '~') {
                    offset++;
                    break;
                } else {
                    perm_ok = 0;
                    break;
                }
            }
            if (!flags || !perm_ok) {
                errno = EINVAL;
                return C_ERR;
            }
        } else {
            flags = ACL_ALL_PERMISSION;
        }

        if (ACLStringHasSpaces(op+offset,oplen-offset)) {
            errno = EINVAL;
            return C_ERR;
        }
        keyPattern *newpat = ACLKeyPatternCreate(sdsnewlen(op+offset,oplen-offset), flags);
        listNode *ln = listSearchKey(selector->patterns,newpat);
        /* 避免多次重复添加相同的键模式。 */
        if (ln == NULL) {
            listAddNodeTail(selector->patterns,newpat);
        } else {
            ((keyPattern *)listNodeValue(ln))->flags |= flags;
            ACLKeyPatternFree(newpat);
        }
        selector->flags &= ~SELECTOR_FLAG_ALLKEYS;
    } else if (op[0] == '&') {
        if (selector->flags & SELECTOR_FLAG_ALLCHANNELS) {
            errno = EISDIR;
            return C_ERR;
        }
        if (ACLStringHasSpaces(op+1,oplen-1)) {
            errno = EINVAL;
            return C_ERR;
        }
        sds newpat = sdsnewlen(op+1,oplen-1);
        listNode *ln = listSearchKey(selector->channels,newpat);
        /* 避免多次重复添加相同的通道模式。 */
        if (ln == NULL)
            listAddNodeTail(selector->channels,newpat);
        else
            sdsfree(newpat);
        selector->flags &= ~SELECTOR_FLAG_ALLCHANNELS;
    } else if (op[0] == '+' && op[1] != '@') {
        if (strrchr(op,'|') == NULL) {
            struct redisCommand *cmd = ACLLookupCommand(op+1);
            if (cmd == NULL) {
                errno = ENOENT;
                return C_ERR;
            }
            ACLChangeSelectorPerm(selector,cmd,1);
            ACLUpdateCommandRules(selector,cmd->fullname,1);
        } else {
            /* Split the command and subcommand parts. */
            char *copy = zstrdup(op+1);
            char *sub = strrchr(copy,'|');
            sub[0] = '\0';
            sub++;

            struct redisCommand *cmd = ACLLookupCommand(copy);

            /* 检查命令是否存在。我们无法检查first-arg是否有效。 */
            if (cmd == NULL) {
                zfree(copy);
                errno = ENOENT;
                return C_ERR;
            }

            /* 我们不支持允许子命令的first-arg */
            if (cmd->parent) {
                zfree(copy);
                errno = ECHILD;
                return C_ERR;
            }

            /* 子命令不能为空，所以像DEBUG|这样的情况当然是语法错误。 */
            if (strlen(sub) == 0) {
                zfree(copy);
                errno = EINVAL;
                return C_ERR;
            }

            if (cmd->subcommands_dict) {
                /* 如果用户尝试允许有效的子命令，我们可以直接添加其唯一ID */
                cmd = ACLLookupCommand(op+1);
                if (cmd == NULL) {
                    zfree(copy);
                    errno = ENOENT;
                    return C_ERR;
                }
                ACLChangeSelectorPerm(selector,cmd,1);
            } else {
                /* 如果用户尝试使用ACL机制来阻止SELECT（除了SELECT 0）或
             * 阻止DEBUG（除了DEBUG OBJECT）（DEBUG子命令目前不被视为
             * 子命令），我们使用allowed_firstargs机制。 */

                /* 将first-arg添加到有效列表中。 */
                serverLog(LL_WARNING, "弃用警告：允许否则被阻止的命令的第一个参数是对ACL的滥用，" \
                                      "并且可能在未来被禁用（违规者：+%s）", op+1);
                ACLAddAllowedFirstArg(selector,cmd->id,sub);
            }
            ACLUpdateCommandRules(selector,op+1,1);
            zfree(copy);
        }
    } else if (op[0] == '-' && op[1] != '@') {
        struct redisCommand *cmd = ACLLookupCommand(op+1);
        if (cmd == NULL) {
            errno = ENOENT;
            return C_ERR;
        }
        ACLChangeSelectorPerm(selector,cmd,0);
        ACLUpdateCommandRules(selector,cmd->fullname,0);
    } else if ((op[0] == '+' || op[0] == '-') && op[1] == '@') {
        int bitval = op[0] == '+' ? 1 : 0;
        if (ACLSetSelectorCategory(selector,op+1,bitval) == C_ERR) {
            errno = ENOENT;
            return C_ERR;
        }
    } else {
        errno = EINVAL;
        return C_ERR;
    }
    return C_OK;
}

/* 根据字符串"op"设置用户属性。以下是不同字符串将执行的操作的描述：
 *
 * on           启用用户：可以以此用户身份进行身份验证
 * off          禁用用户：不再可能以此用户身份进行身份验证，
 *              但已通过身份验证的连接仍然有效
 * skip-sanitize-payload    跳过RESTORE转储负载的清理
 * sanitize-payload         清理RESTORE转储负载（默认值）
 * ><password>  将此密码添加到用户的有效密码列表中
 *              例如>mypass将"mypass"添加到列表中
 *              此指令清除"nopass"标志（见后文）
 * #<hash>      将此密码哈希添加到用户的有效哈希列表中
 *              如果您之前已计算了哈希，并且不想以明文形式存储它，这很有用
 *              此指令清除"nopass"标志（见后文）
 * <<password>  从此用户的有效密码列表中删除此密码
 * !<hash>      从此用户的有效密码列表中删除此哈希密码
 *              当您只想通过哈希删除密码而完全不知道其明文版本时，这很有用
 * nopass       移除用户设置的所有密码，并且将用户标记为不需要密码：
 *              这意味着任何密码都可以对该用户起作用
 *              如果默认用户使用此指令，则每个新连接将立即使用默认用户进行身份验证，
 *              无需任何显式的AUTH命令
 *              请注意，"resetpass"指令将清除此条件
 * resetpass    清除允许的密码列表。此外，移除"nopass"状态
 *              "resetpass"后，用户没有关联的密码，
 *              如果不添加密码（或稍后将其设置为"nopass"），则无法进行身份验证
 * reset        执行以下操作：resetpass、resetkeys、resetchannels、
 *              allchannels（如果设置了acl-pubsub-default）、off、clearselectors、-@all
 *              用户返回到创建后立即拥有的状态
 * (<options>)  创建一个带有括号内指定选项的新选择器，并将其附加到用户
 *              每个选项应该以空格分隔。第一个字符必须是(，最后一个字符必须是)
 * clearselectors          移除所有当前附加的选择器
 *                         注意，这不会更改"根"用户权限，
 *                         根权限是直接应用于用户的权限（括号外）
 * 
 * 此函数也可以指定选择器选项，在这种情况下，它们会更新用户的根选择器。
 *
 * 'op'字符串必须以null结尾。'oplen'参数应指定'op'字符串的长度，
 * 以防调用者需要传递二进制数据（例如>password形式可能使用二进制密码）。
 * 否则，该字段可以设置为-1，函数将使用strlen()确定长度。
 *
 * 如果理解了要执行的操作（因为'op'字符串有意义），函数返回C_OK。
 * 如果操作未知或有语法错误，则返回C_ERR。
 *
 * 当返回错误时，errno设置为以下值：
 *
 * EINVAL：指定的操作码不被理解，或者键/通道模式无效（包含不允许的字符）。
 * ENOENT：使用+或-提供的命令名称或命令类别不被识别。
 * EEXIST：在已经添加了"*"之后，您正在添加键模式。这几乎肯定是用户端的错误。
 * EISDIR：在已经添加了"*"之后，您正在添加通道模式。这几乎肯定是用户端的错误。
 * ENODEV：您尝试从用户中删除的密码不存在。
 * EBADMSG：您尝试添加的哈希不是有效哈希。
 * ECHILD：尝试允许子命令的特定第一个参数
 */
int ACLSetUser(user *u, const char *op, ssize_t oplen) {
    /* as we are changing the ACL, the old generated string is now invalid */
    if (u->acl_string) {
        decrRefCount(u->acl_string);
        u->acl_string = NULL;
    }

    if (oplen == -1) oplen = strlen(op);
    if (oplen == 0) return C_OK; /* Empty string is a no-operation. */
    if (!strcasecmp(op,"on")) {
        u->flags |= USER_FLAG_ENABLED;
        u->flags &= ~USER_FLAG_DISABLED;
    } else if (!strcasecmp(op,"off")) {
        u->flags |= USER_FLAG_DISABLED;
        u->flags &= ~USER_FLAG_ENABLED;
    } else if (!strcasecmp(op,"skip-sanitize-payload")) {
        u->flags |= USER_FLAG_SANITIZE_PAYLOAD_SKIP;
        u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD;
    } else if (!strcasecmp(op,"sanitize-payload")) {
        u->flags &= ~USER_FLAG_SANITIZE_PAYLOAD_SKIP;
        u->flags |= USER_FLAG_SANITIZE_PAYLOAD;
    } else if (!strcasecmp(op,"nopass")) {
        u->flags |= USER_FLAG_NOPASS;
        listEmpty(u->passwords);
    } else if (!strcasecmp(op,"resetpass")) {
        u->flags &= ~USER_FLAG_NOPASS;
        listEmpty(u->passwords);
    } else if (op[0] == '>' || op[0] == '#') {
        sds newpass;
        if (op[0] == '>') {
            newpass = ACLHashPassword((unsigned char*)op+1,oplen-1);
        } else {
            if (ACLCheckPasswordHash((unsigned char*)op+1,oplen-1) == C_ERR) {
                errno = EBADMSG;
                return C_ERR;
            }
            newpass = sdsnewlen(op+1,oplen-1);
        }

        listNode *ln = listSearchKey(u->passwords,newpass);
        /* 避免多次重复添加相同的密码。 */
        if (ln == NULL)
            listAddNodeTail(u->passwords,newpass);
        else
            sdsfree(newpass);
        u->flags &= ~USER_FLAG_NOPASS;
    } else if (op[0] == '<' || op[0] == '!') {
        sds delpass;
        if (op[0] == '<') {
            delpass = ACLHashPassword((unsigned char*)op+1,oplen-1);
        } else {
            if (ACLCheckPasswordHash((unsigned char*)op+1,oplen-1) == C_ERR) {
                errno = EBADMSG;
                return C_ERR;
            }
            delpass = sdsnewlen(op+1,oplen-1);
        }
        listNode *ln = listSearchKey(u->passwords,delpass);
        sdsfree(delpass);
        if (ln) {
            listDelNode(u->passwords,ln);
        } else {
            errno = ENODEV;
            return C_ERR;
        }
    } else if (op[0] == '(' && op[oplen - 1] == ')') {
        aclSelector *selector = aclCreateSelectorFromOpSet(op, oplen);
        if (!selector) {
            /* 没有设置errno，从内部错误传播。 */
            return C_ERR;
        }
        listAddNodeTail(u->selectors, selector);
        return C_OK;
    } else if (!strcasecmp(op,"clearselectors")) {
        listIter li;
        listNode *ln;
        listRewind(u->selectors,&li);
        /* 必须有一个根选择器 */
        serverAssert(listNext(&li));
        while((ln = listNext(&li))) {
            listDelNode(u->selectors, ln);
        }
        return C_OK;
    } else if (!strcasecmp(op,"reset")) {
        serverAssert(ACLSetUser(u,"resetpass",-1) == C_OK);
        serverAssert(ACLSetUser(u,"resetkeys",-1) == C_OK);
        serverAssert(ACLSetUser(u,"resetchannels",-1) == C_OK);
        if (server.acl_pubsub_default & SELECTOR_FLAG_ALLCHANNELS)
            serverAssert(ACLSetUser(u,"allchannels",-1) == C_OK);
        serverAssert(ACLSetUser(u,"off",-1) == C_OK);
        serverAssert(ACLSetUser(u,"sanitize-payload",-1) == C_OK);
        serverAssert(ACLSetUser(u,"clearselectors",-1) == C_OK);
        serverAssert(ACLSetUser(u,"-@all",-1) == C_OK);
    } else {
        aclSelector *selector = ACLUserGetRootSelector(u);
        if (ACLSetSelector(selector, op, oplen) == C_ERR) {
            return C_ERR;
        }
    }
    return C_OK;
}

/* 根据ACLSetUser()函数在出错时设置的errno值，返回错误描述。 */
const char *ACLSetUserStringError(void) {
    const char *errmsg = "Wrong format";
    if (errno == ENOENT)
        errmsg = "Unknown command or category name in ACL";
    else if (errno == EINVAL)
        errmsg = "Syntax error";
    else if (errno == EEXIST)
        errmsg = "Adding a pattern after the * pattern (or the "
                 "'allkeys' flag) is not valid and does not have any "
                 "effect. Try 'resetkeys' to start with an empty "
                 "list of patterns";
    else if (errno == EISDIR)
        errmsg = "Adding a pattern after the * pattern (or the "
                 "'allchannels' flag) is not valid and does not have any "
                 "effect. Try 'resetchannels' to start with an empty "
                 "list of channels";
    else if (errno == ENODEV)
        errmsg = "The password you are trying to remove from the user does "
                 "not exist";
    else if (errno == EBADMSG)
        errmsg = "The password hash must be exactly 64 characters and contain "
                 "only lowercase hexadecimal characters";
    else if (errno == EALREADY)
        errmsg = "Duplicate user found. A user can only be defined once in "
                 "config files";
    else if (errno == ECHILD)
        errmsg = "Allowing first-arg of a subcommand is not supported";
    return errmsg;
}

/* 创建默认用户，该用户具有特殊权限。 */
user *ACLCreateDefaultUser(void) {
    user *new = ACLCreateUser("default",7);
    ACLSetUser(new,"+@all",-1);
    ACLSetUser(new,"~*",-1);
    ACLSetUser(new,"&*",-1);
    ACLSetUser(new,"on",-1);
    ACLSetUser(new,"nopass",-1);
    return new;
}

/* ACL子系统的初始化。 */
void ACLInit(void) {
    Users = raxNew();
    UsersToLoad = listCreate();
    ACLInitCommandCategories();
    listSetMatchMethod(UsersToLoad, ACLListMatchLoadedUser);
    ACLLog = listCreate();
    DefaultUser = ACLCreateDefaultUser();
}

/* 检查用户名和密码对，如果有效则返回C_OK，
 * 否则返回C_ERR并设置errno为：
 *
 *  EINVAL：如果用户名密码不匹配
 *  ENOENT：如果指定的用户根本不存在
 */
int ACLCheckUserCredentials(robj *username, robj *password) {
    user *u = ACLGetUserByName(username->ptr,sdslen(username->ptr));
    if (u == NULL) {
        errno = ENOENT;
        return C_ERR;
    }

    /* Disabled users can't login. */
    if (u->flags & USER_FLAG_DISABLED) {
        errno = EINVAL;
        return C_ERR;
    }

    /* If the user is configured to don't require any password, we
     * are already fine here. */
    if (u->flags & USER_FLAG_NOPASS) return C_OK;

    /* Check all the user passwords for at least one to match. */
    listIter li;
    listNode *ln;
    listRewind(u->passwords,&li);
    sds hashed = ACLHashPassword(password->ptr,sdslen(password->ptr));
    while((ln = listNext(&li))) {
        sds thispass = listNodeValue(ln);
        if (!time_independent_strcmp(hashed, thispass, HASH_PASSWORD_LEN)) {
            sdsfree(hashed);
            return C_OK;
        }
    }
    sdsfree(hashed);

    /* If we reached this point, no password matched. */
    errno = EINVAL;
    return C_ERR;
}

/* 如果提供了`err`，它将被添加为客户端的错误回复。
 * 否则，将添加标准的Auth错误作为回复。 */
void addAuthErrReply(client *c, robj *err) {
    if (clientHasPendingReplies(c)) return;
    if (!err) {
        addReplyError(c, "-WRONGPASS invalid username-password pair or user is disabled.");
        return;
    }
    addReplyError(c, err->ptr);
}

/* 这类似于ACLCheckUserCredentials()，但是如果用户名/密码
 * 正确，连接将被置于已认证状态，并且连接的用户引用将被填充。
 *
 * 成功时返回AUTH_OK（有效的用户名/密码对），否则返回AUTH_ERR。 */
int checkPasswordBasedAuth(client *c, robj *username, robj *password) {
    if (ACLCheckUserCredentials(username,password) == C_OK) {
        c->authenticated = 1;
        c->user = ACLGetUserByName(username->ptr,sdslen(username->ptr));
        moduleNotifyUserChanged(c);
        return AUTH_OK;
    } else {
        addACLLogEntry(c,ACL_DENIED_AUTH,(c->flags & CLIENT_MULTI) ? ACL_LOG_CTX_MULTI : ACL_LOG_CTX_TOPLEVEL,0,username->ptr,NULL);
        return AUTH_ERR;
    }
}

/* 尝试对用户进行身份验证 - 首先通过基于模块的身份验证，
 * 然后，如果需要，使用正常的基于密码的身份验证。
 * 返回以下代码之一：
 * AUTH_OK - 表示身份验证成功
 * AUTH_ERR - 表示身份验证失败
 * AUTH_BLOCKED - 表示模块身份验证正在通过阻塞实现进行中
 */
int ACLAuthenticateUser(client *c, robj *username, robj *password, robj **err) {
    int result = checkModuleAuthentication(c, username, password, err);
    /* If authentication was not handled by any Module, attempt normal password based auth. */
    if (result == AUTH_NOT_HANDLED) {
        result = checkPasswordBasedAuth(c, username, password);
    }
    return result;
}

/* 出于ACL目的，每个用户都有一个位图，其中包含该用户
 * 被允许执行的命令。为了填充位图，每个命令
 * 都应该有一个分配的ID（用于索引位图）。此函数
 * 创建这样的ID：它使用顺序ID，对相同的命令名重用相同的ID，
 * 以便在模块被卸载并稍后重新加载的情况下，命令保留相同的ID。
 *
 * 该函数不取得'cmdname' SDS字符串的所有权。
 */
unsigned long ACLGetCommandID(sds cmdname) {
    sds lowername = sdsdup(cmdname);
    sdstolower(lowername);
    if (commandId == NULL) commandId = raxNew();
    void *id;
    if (raxFind(commandId,(unsigned char*)lowername,sdslen(lowername),&id)) {
        sdsfree(lowername);
        return (unsigned long)id;
    }
    raxInsert(commandId,(unsigned char*)lowername,strlen(lowername),
              (void*)nextid,NULL);
    sdsfree(lowername);
    unsigned long thisid = nextid;
    nextid++;

    /* 我们从不分配用户命令位图结构中的最后一位，
     * 这样我们以后可以检查该位是否设置，了解用户的当前ACL是否
     * 是以+@all开始（添加所有可能的命令，然后减去其他单个命令或类别），
     * 或者ACL是从头开始添加命令和命令类别创建的，默认不允许将来的命令
     * （通过模块加载）。这在使用ACL SAVE重写ACL时很有用。 */
    if (nextid == USER_COMMAND_BITS_COUNT-1) nextid++;
    return thisid;
}

/* 清除命令ID表并将nextid重置为0。 */
void ACLClearCommandID(void) {
    if (commandId) raxFree(commandId);
    commandId = NULL;
    nextid = 0;
}

/* 通过用户名返回用户，如果用户不存在则返回NULL。 */
user *ACLGetUserByName(const char *name, size_t namelen) {
    void *myuser = NULL;
    raxFind(Users,(unsigned char*)name,namelen,&myuser);
    return myuser;
}

/* =============================================================================
 * ACL权限检查
 * ==========================================================================*/

/* 检查选择器是否可以访问该键。
 *
 * 如果选择器可以访问该键，返回ACL_OK，否则
 * 返回ACL_DENIED_KEY。 */
static int ACLSelectorCheckKey(aclSelector *selector, const char *key, int keylen, int keyspec_flags) {
    /* 选择器可以访问任何键 */
    if (selector->flags & SELECTOR_FLAG_ALLKEYS) return ACL_OK;

    listIter li;
    listNode *ln;
    listRewind(selector->patterns,&li);

    int key_flags = 0;
    if (keyspec_flags & CMD_KEY_ACCESS) key_flags |= ACL_READ_PERMISSION;
    if (keyspec_flags & CMD_KEY_INSERT) key_flags |= ACL_WRITE_PERMISSION;
    if (keyspec_flags & CMD_KEY_DELETE) key_flags |= ACL_WRITE_PERMISSION;
    if (keyspec_flags & CMD_KEY_UPDATE) key_flags |= ACL_WRITE_PERMISSION;

    /* 给定的键是否代表一组键的前缀 */
    int prefix = keyspec_flags & CMD_KEY_PREFIX;

    /* 对照每个模式测试此键。 */
    while((ln = listNext(&li))) {
        keyPattern *pattern = listNodeValue(ln);
        if ((pattern->flags & key_flags) != key_flags)
            continue;
        size_t plen = sdslen(pattern->pattern);
        if (prefix) {
            if (prefixmatch(pattern->pattern,plen,key,keylen,0))
                return ACL_OK;
        } else {
            if (stringmatchlen(pattern->pattern, plen, key, keylen, 0))
                return ACL_OK;
        }
    }
    return ACL_DENIED_KEY;
}

/* 检查提供的选择器是否具有标志中指定的对键空间中所有键的访问权限。
 * 例如，CMD_KEY_READ访问需要选择器被授予'%R~*'、'~*'或allkeys。
 * 如果该选择器满足所有访问标志，则返回1，否则返回0。
 */
static int ACLSelectorHasUnrestrictedKeyAccess(aclSelector *selector, int flags) {
    /* 选择器可以访问任何键 */
    if (selector->flags & SELECTOR_FLAG_ALLKEYS) return 1;

    listIter li;
    listNode *ln;
    listRewind(selector->patterns,&li);

    int access_flags = 0;
    if (flags & CMD_KEY_ACCESS) access_flags |= ACL_READ_PERMISSION;
    if (flags & CMD_KEY_INSERT) access_flags |= ACL_WRITE_PERMISSION;
    if (flags & CMD_KEY_DELETE) access_flags |= ACL_WRITE_PERMISSION;
    if (flags & CMD_KEY_UPDATE) access_flags |= ACL_WRITE_PERMISSION;

    /* Test this key against every pattern. */
    while((ln = listNext(&li))) {
        keyPattern *pattern = listNodeValue(ln);
        if ((pattern->flags & access_flags) != access_flags)
            continue;
        if (!strcmp(pattern->pattern,"*")) {
           return 1;
       }
    }
    return 0;
}

/* 对照提供的通道列表检查一个通道。is_pattern参数
 * 只应在订阅时使用（发布时不使用），它控制输入通道是被评估为通道模式
 * （如PSUBSCRIBE中）还是普通通道名称（如SUBSCRIBE中）。
 * 
 * 请注意，像PUBLISH或SUBSCRIBE中的普通通道名称可以与
 * ACL通道模式进行匹配，但PSUBSCRIBE中提供的模式只能作为字面值与
 * ACL模式进行匹配（使用普通字符串比较）。 */
static int ACLCheckChannelAgainstList(list *reference, const char *channel, int channellen, int is_pattern) {
    listIter li;
    listNode *ln;

    listRewind(reference, &li);
    while((ln = listNext(&li))) {
        sds pattern = listNodeValue(ln);
        size_t plen = sdslen(pattern);
        /* 通道模式在列表中与通道进行字面匹配。
         * 普通通道执行模式匹配。 */
        if ((is_pattern && !strcmp(pattern,channel)) || 
            (!is_pattern && stringmatchlen(pattern,plen,channel,channellen,0)))
        {
            return ACL_OK;
        }
    }
    return ACL_DENIED_CHANNEL;
}

/* 为了防止重复调用getKeysResult，在对各种选择器的调用之间
 * 维护一个缓存。 */
typedef struct {
    int keys_init;
    getKeysResult keys;
} aclKeyResultCache;

void initACLKeyResultCache(aclKeyResultCache *cache) {
    cache->keys_init = 0;
}

void cleanupACLKeyResultCache(aclKeyResultCache *cache) {
    if (cache->keys_init) getKeysFreeResult(&(cache->keys));
}

/* 检查命令是否可以根据与指定选择器关联的ACL执行。
 *
 * 如果选择器可以执行命令，返回ACL_OK，否则
 * 返回ACL_DENIED_CMD、ACL_DENIED_KEY或ACL_DENIED_CHANNEL：
 * 第一个表示选择器不允许运行该命令，无法执行命令；
 * 第二个和第三个表示命令被拒绝，因为选择器试图
 * 访问不在指定模式中的键或通道。 */
static int ACLSelectorCheckCmd(aclSelector *selector, struct redisCommand *cmd, robj **argv, int argc, int *keyidxptr, aclKeyResultCache *cache) {
    uint64_t id = cmd->id;
    int ret;
    if (!(selector->flags & SELECTOR_FLAG_ALLCOMMANDS) && !(cmd->flags & CMD_NO_AUTH)) {
        /* 如果位未设置，我们需要进一步检查，以防命令只允许特定的第一个参数。 */
        if (ACLGetSelectorCommandBit(selector,id) == 0) {
            /* 检查第一个参数是否匹配。 */
            if (argc < 2 ||
                selector->allowed_firstargs == NULL ||
                selector->allowed_firstargs[id] == NULL)
            {
                return ACL_DENIED_CMD;
            }

            long subid = 0;
            while (1) {
                if (selector->allowed_firstargs[id][subid] == NULL)
                    return ACL_DENIED_CMD;
                int idx = cmd->parent ? 2 : 1;
                if (!strcasecmp(argv[idx]->ptr,selector->allowed_firstargs[id][subid]))
                    break; /* First argument match found. Stop here. */
                subid++;
            }
        }
    }

    /* 检查用户是否可以执行显式触及命令参数中提到的键的命令。 */
    if (!(selector->flags & SELECTOR_FLAG_ALLKEYS) && doesCommandHaveKeys(cmd)) {
        if (!(cache->keys_init)) {
            cache->keys = (getKeysResult) GETKEYS_RESULT_INIT;
            getKeysFromCommandWithSpecs(cmd, argv, argc, GET_KEYSPEC_DEFAULT, &(cache->keys));
            cache->keys_init = 1;
        }
        getKeysResult *result = &(cache->keys);
        keyReference *resultidx = result->keys;
        for (int j = 0; j < result->numkeys; j++) {
            int idx = resultidx[j].pos;
            ret = ACLSelectorCheckKey(selector, argv[idx]->ptr, sdslen(argv[idx]->ptr), resultidx[j].flags);
            if (ret != ACL_OK) {
                if (keyidxptr) *keyidxptr = resultidx[j].pos;
                return ret;
            }
        }
    }

    /* 检查用户是否可以执行显式触及命令参数中提到的通道的命令 */
    const int channel_flags = CMD_CHANNEL_PUBLISH | CMD_CHANNEL_SUBSCRIBE;
    if (!(selector->flags & SELECTOR_FLAG_ALLCHANNELS) && doesCommandHaveChannelsWithFlags(cmd, channel_flags)) {
        getKeysResult channels = (getKeysResult) GETKEYS_RESULT_INIT;
        getChannelsFromCommand(cmd, argv, argc, &channels);
        keyReference *channelref = channels.keys;
        for (int j = 0; j < channels.numkeys; j++) {
            int idx = channelref[j].pos;
            if (!(channelref[j].flags & channel_flags)) continue;
            int is_pattern = channelref[j].flags & CMD_CHANNEL_PATTERN;
            int ret = ACLCheckChannelAgainstList(selector->channels, argv[idx]->ptr, sdslen(argv[idx]->ptr), is_pattern);
            if (ret != ACL_OK) {
                if (keyidxptr) *keyidxptr = channelref[j].pos;
                getKeysFreeResult(&channels);
                return ret;
            }
        }
        getKeysFreeResult(&channels);
    }
    return ACL_OK;
}

/* 根据与指定用户关联的ACL（按照keyspec访问标志），检查客户端是否可以访问该键。
 *
 * 如果用户可以访问该键，返回ACL_OK，否则
 * 返回ACL_DENIED_KEY。 */
int ACLUserCheckKeyPerm(user *u, const char *key, int keylen, int flags) {
    listIter li;
    listNode *ln;

    /* 如果没有关联用户，连接可以运行任何命令。 */
    if (u == NULL) return ACL_OK;

    /* Check all of the selectors */
    listRewind(u->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        if (ACLSelectorCheckKey(s, key, keylen, flags) == ACL_OK) {
            return ACL_OK;
        }
    }
    return ACL_DENIED_KEY;
}

/* 检查用户是否可以执行给定命令，并附加一个限制：
 * 用户必须还具有标志中指定的对键空间中任何键的访问权限。
 * 例如，CMD_KEY_READ访问除了命令所需的访问权限外，还需要被授予
 * '%R~*'、'~*'或allkeys。如果用户有权限则返回1，否则返回0。
 */
int ACLUserCheckCmdWithUnrestrictedKeyAccess(user *u, struct redisCommand *cmd, robj **argv, int argc, int flags) {
    listIter li;
    listNode *ln;
    int local_idxptr;

    /* 如果没有关联用户，连接可以运行任何命令。 */
    if (u == NULL) return 1;

    /* 对于多个选择器，我们在选择器调用之间缓存键结果，以防止重复查找。 */
    aclKeyResultCache cache;
    initACLKeyResultCache(&cache);

    /* 顺序检查每个选择器 */
    listRewind(u->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        int acl_retval = ACLSelectorCheckCmd(s, cmd, argv, argc, &local_idxptr, &cache);
        if (acl_retval == ACL_OK && ACLSelectorHasUnrestrictedKeyAccess(s, flags)) {
            cleanupACLKeyResultCache(&cache);
            return 1;
        }
    }
    cleanupACLKeyResultCache(&cache);
    return 0;
}

/* 根据与指定用户关联的ACL，检查客户端是否可以访问该通道。
 *
 * 如果用户可以访问该通道，返回ACL_OK，否则
 * 返回ACL_DENIED_CHANNEL。 */
int ACLUserCheckChannelPerm(user *u, sds channel, int is_pattern) {
    listIter li;
    listNode *ln;

    /* 如果没有关联用户，连接可以运行任何命令。 */
    if (u == NULL) return ACL_OK;

    /* Check all of the selectors */
    listRewind(u->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        /* 选择器可以运行任何键 */
        if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return ACL_OK;

        /* 否则，遍历选择器列表并检查每个通道 */
        if (ACLCheckChannelAgainstList(s->channels, channel, sdslen(channel), is_pattern) == ACL_OK) {
            return ACL_OK;
        }
    }
    return ACL_DENIED_CHANNEL;
}

/* 较低级别的API，用于检查指定用户是否能够执行给定命令。
 *
 * 如果命令未能通过ACL检查，idxptr将被设置为导致失败的第一个argv条目，
 * 如果是命令本身失败则为0，或者是导致失败的键/通道的索引。 */
int ACLCheckAllUserCommandPerm(user *u, struct redisCommand *cmd, robj **argv, int argc, int *idxptr) {
    listIter li;
    listNode *ln;

    /* If there is no associated user, the connection can run anything. */
    if (u == NULL) return ACL_OK;

    /* 我们必须选择一个错误进行记录，选择逻辑如下：
     * 1) 如果没有选择器可以执行命令，返回命令错误。
     * 2) 返回没有选择器可以匹配的最后一个键或通道。 */
    int relevant_error = ACL_DENIED_CMD;
    int local_idxptr = 0, last_idx = 0;

    /* 对于多个选择器，我们在选择器调用之间缓存键结果，以防止重复查找。 */
    aclKeyResultCache cache;
    initACLKeyResultCache(&cache);

    /* 顺序检查每个选择器 */
    listRewind(u->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        int acl_retval = ACLSelectorCheckCmd(s, cmd, argv, argc, &local_idxptr, &cache);
        if (acl_retval == ACL_OK) {
            cleanupACLKeyResultCache(&cache);
            return ACL_OK;
        }
        if (acl_retval > relevant_error ||
            (acl_retval == relevant_error && local_idxptr > last_idx))
        {
            relevant_error = acl_retval;
            last_idx = local_idxptr;
        }
    }

    *idxptr = last_idx;
    cleanupACLKeyResultCache(&cache);
    return relevant_error;
}

/* 用于检查客户端是否可以执行排队命令的高级API */
int ACLCheckAllPerm(client *c, int *idxptr) {
    return ACLCheckAllUserCommandPerm(c->user, c->cmd, c->argv, c->argc, idxptr);
}

/* 如果'new'用户可以访问'original'用户能访问的所有通道，则返回NULL；
   否则返回新用户可以访问的通道列表 */
list *getUpcomingChannelList(user *new, user *original) {
    listIter li, lpi;
    listNode *ln, *lpn;

    /* 优化：我们检查是否有选择器拥有所有通道权限。 */
    listRewind(new->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        if (s->flags & SELECTOR_FLAG_ALLCHANNELS) return NULL;
    }

    /* 接下来，检查新的通道列表是否是原始列表的严格超集。
     * 这是通过创建一个包含新用户所有通道的"upcoming"列表，
     * 然后将每个现有通道与该列表进行比较来完成的。 */
    list *upcoming = listCreate();
    listRewind(new->selectors,&li);
    while((ln = listNext(&li))) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        listRewind(s->channels, &lpi);
        while((lpn = listNext(&lpi))) {
            listAddNodeTail(upcoming, listNodeValue(lpn));
        }
    }

    int match = 1;
    listRewind(original->selectors,&li);
    while((ln = listNext(&li)) && match) {
        aclSelector *s = (aclSelector *) listNodeValue(ln);
        /* 如果任何原始选择器拥有所有通道权限，但新选择器没有
             * （这在函数的前面部分已经检查过），那么新列表不是
             * 原始列表的严格超集。 */
        if (s->flags & SELECTOR_FLAG_ALLCHANNELS) {
            match = 0;
            break;
        }
        listRewind(s->channels, &lpi);
        while((lpn = listNext(&lpi)) && match) {
            if (!listSearchKey(upcoming, listNodeValue(lpn))) {
                match = 0;
                break;
            }
        }
    }

    if (match) {
        /* 所有通道都已匹配，不需要终止客户端。 */
        listRelease(upcoming);
        return NULL;
    }

    return upcoming;
}

/* 检查客户端是否应该被终止，因为它订阅了过去被允许但不在
 * `upcoming`通道列表中的通道。 */
int ACLShouldKillPubsubClient(client *c, list *upcoming) {
    robj *o;
    int kill = 0;

    if (getClientType(c) == CLIENT_TYPE_PUBSUB) {
        /* Check for pattern violations. */
        dictIterator *di = dictGetIterator(c->pubsub_patterns);
        dictEntry *de;
        while (!kill && ((de = dictNext(di)) != NULL)) {
            o = dictGetKey(de);
            int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
            kill = (res == ACL_DENIED_CHANNEL);
        }
        dictReleaseIterator(di);

        /* Check for channel violations. */
        if (!kill) {
            /* Check for global channels violation. */
            di = dictGetIterator(c->pubsub_channels);

            while (!kill && ((de = dictNext(di)) != NULL)) {
                o = dictGetKey(de);
                int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
                kill = (res == ACL_DENIED_CHANNEL);
            }
            dictReleaseIterator(di);
        }
        if (!kill) {
            /* Check for shard channels violation. */
            di = dictGetIterator(c->pubsubshard_channels);
            while (!kill && ((de = dictNext(di)) != NULL)) {
                o = dictGetKey(de);
                int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
                kill = (res == ACL_DENIED_CHANNEL);
            }
            dictReleaseIterator(di);
        }

        if (kill) {
            return 1;
        }
    }
    return 0;
}

/* 检查用户现有的pub/sub客户端是否违反了通过upcoming参数指定的
 * ACL pub/sub权限，如果是，则终止它们。 */
void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
    /* 如果没有订阅者，则不执行任何操作。 */
    if (pubsubTotalSubscriptions() == 0)
        return;

    list *channels = getUpcomingChannelList(new, original);
    /* 如果新用户的pubsub权限是原始权限的严格超集，则提前返回。 */
    if (!channels)
        return;

    listIter li;
    listNode *ln;

    /* 权限已更改，因此我们需要遍历所有客户端
     * 并断开不再有效的客户端连接。
     * 扫描所有连接的客户端以查找用户的pub/sub。 */
    listRewind(server.clients,&li);
    while ((ln = listNext(&li)) != NULL) {
        client *c = listNodeValue(ln);
        if (c->user != original)
            continue;
        if (ACLShouldKillPubsubClient(c, channels))
            deauthenticateAndCloseClient(c);
    }

    listRelease(channels);
}

/* =============================================================================
 * ACL加载/保存函数
 * ==========================================================================*/


/* 选择器定义应该作为单个参数发送，但是我们将宽容处理并尝试查找
 * 分散在多个参数中的选择器定义，因为这使得ACL SETUSER以及从配置文件加载时
 * 的用户体验更简单。
 * 
 * 此函数接收一个排除用户名的ACL操作符数组，并合并分散在多个参数中的选择器操作。
 * 返回值是一个新的SDS数组，其长度设置为传入的merged_argc。未修改的参数仍会被复制。
 * 如果存在不匹配的括号，返回NULL，并且invalid_idx将设置为带开始括号的参数的索引。 */
sds *ACLMergeSelectorArguments(sds *argv, int argc, int *merged_argc, int *invalid_idx) {
    *merged_argc = 0;
    int open_bracket_start = -1;

    sds *acl_args = (sds *) zmalloc(sizeof(sds) * argc);

    sds selector = NULL;
    for (int j = 0; j < argc; j++) {
        char *op = argv[j];

        if (open_bracket_start == -1 &&
            (op[0] == '(' && op[sdslen(op) - 1] != ')')) {
            selector = sdsdup(argv[j]);
            open_bracket_start = j;
            continue;
        }

        if (open_bracket_start != -1) {
            selector = sdscatfmt(selector, " %s", op);
            if (op[sdslen(op) - 1] == ')') {
                open_bracket_start = -1;
                acl_args[*merged_argc] = selector;                        
                (*merged_argc)++;
            }
            continue;
        }

        acl_args[*merged_argc] = sdsdup(argv[j]);
        (*merged_argc)++;
    }

    if (open_bracket_start != -1) {
        for (int i = 0; i < *merged_argc; i++) sdsfree(acl_args[i]);
        zfree(acl_args);
        sdsfree(selector);
        if (invalid_idx) *invalid_idx = open_bracket_start;
        return NULL;
    }

    return acl_args;
}

/* 接收一个已按空格分割的ACL字符串并将其添加到给定用户
 * 如果用户对象为NULL，将使用给定的用户名创建用户
 *
 * 如果ACL字符串无法解析，返回错误作为sds字符串
 */
sds ACLStringSetUser(user *u, sds username, sds *argv, int argc) {
    serverAssert(u != NULL || username != NULL);

    sds error = NULL;

    int merged_argc = 0, invalid_idx = 0;
    sds *acl_args = ACLMergeSelectorArguments(argv, argc, &merged_argc, &invalid_idx);

    if (!acl_args) {
        return sdscatfmt(sdsempty(),
                         "Unmatched parenthesis in acl selector starting "
                         "at '%s'.", (char *) argv[invalid_idx]);
    }

    /* 创建一个临时用户来验证和暂存所有更改，
     * 然后再应用到现有用户或创建新用户。如果所有参数有效，
     * 则所有用户参数将一起应用。如果有任何错误，则不应用任何更改。 */
    user *tempu = ACLCreateUnlinkedUser();
    if (u) {
        ACLCopyUser(tempu, u);
    }

    for (int j = 0; j < merged_argc; j++) {
        if (ACLSetUser(tempu,acl_args[j],(ssize_t) sdslen(acl_args[j])) != C_OK) {
            const char *errmsg = ACLSetUserStringError();
            error = sdscatfmt(sdsempty(),
                              "Error in ACL SETUSER modifier '%s': %s",
                              (char*)acl_args[j], errmsg);
            goto cleanup;
        }
    }

    /* 如果（部分）通道权限被撤销，已使用该用户认证的pub/sub客户端
     * 可能需要断开连接。 */
    if (u) {
        ACLKillPubsubClientsIfNeeded(tempu, u);
    }

    /* 用我们上面修改的临时用户覆盖实际用户。 */
    if (!u) {
        u = ACLCreateUser(username,sdslen(username));
    }
    serverAssert(u != NULL);

    ACLCopyUser(u, tempu);

cleanup:
    ACLFreeUser(tempu);
    for (int i = 0; i < merged_argc; i++) {
        sdsfree(acl_args[i]);
    }
    zfree(acl_args);

    return error;
}

/* 给定一个描述用户的参数向量，格式如下：
 *
 *      user <用户名> ... ACL规则和标志 ...
 *
 * 此函数进行验证，如果语法有效，则将用户定义附加到列表中供以后加载。
 *
 * 规则会被测试有效性，如果有明显的语法错误，函数返回C_ERR且不执行任何操作，
 * 否则返回C_OK并将用户添加到列表中。
 *
 * 请注意，对于未找到的命令，此函数不会停止，错误将在稍后发出，因为某些命令
 * 可能会在模块加载后才定义。
 *
 * 当检测到错误并返回C_ERR时，如果argc_err不为NULL，函数会通过引用将
 * 导致错误的argv向量索引填充到argc_err参数中。 */
int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
    if (argc < 2 || strcasecmp(argv[0],"user")) {
        if (argc_err) *argc_err = 0;
        return C_ERR;
    }

    if (listSearchKey(UsersToLoad, argv[1])) {
        if (argc_err) *argc_err = 1;
        errno = EALREADY;
        return C_ERR; 
    }

    /* 在尝试处理之前合并选择器 */
    int merged_argc;
    sds *acl_args = ACLMergeSelectorArguments(argv + 2, argc - 2, &merged_argc, argc_err);

    if (!acl_args) {
        return C_ERR;
    }

    /* 尝试在假用户上应用用户规则，看它们是否实际有效。 */
    user *fakeuser = ACLCreateUnlinkedUser();

    for (int j = 0; j < merged_argc; j++) {
        if (ACLSetUser(fakeuser,acl_args[j],sdslen(acl_args[j])) == C_ERR) {
            if (errno != ENOENT) {
                ACLFreeUser(fakeuser);
                if (argc_err) *argc_err = j;
                for (int i = 0; i < merged_argc; i++) sdsfree(acl_args[i]);
                zfree(acl_args);
                return C_ERR;
            }
        }
    }

    /* Rules look valid, let's append the user to the list. */
    sds *copy = zmalloc(sizeof(sds)*(merged_argc + 2));
    copy[0] = sdsdup(argv[1]);
    for (int j = 0; j < merged_argc; j++) copy[j+1] = sdsdup(acl_args[j]);
    copy[merged_argc + 1] = NULL;
    listAddNodeTail(UsersToLoad,copy);
    ACLFreeUser(fakeuser);
    for (int i = 0; i < merged_argc; i++) sdsfree(acl_args[i]);
    zfree(acl_args);
    return C_OK;
}

/* 此函数将加载通过ACLAppendUserForLoading()添加到服务器配置的已配置用户。
 * 加载错误时会记录错误并返回C_ERR，否则返回C_OK。 */
int ACLLoadConfiguredUsers(void) {
    listIter li;
    listNode *ln;
    listRewind(UsersToLoad,&li);
    while ((ln = listNext(&li)) != NULL) {
        sds *aclrules = listNodeValue(ln);
        sds username = aclrules[0];

        if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) {
            serverLog(LL_WARNING,"Spaces not allowed in ACL usernames");
            return C_ERR;
        }

        user *u = ACLCreateUser(username,sdslen(username));
        if (!u) {
            /* 唯一有效的重复用户是默认用户。 */
            serverAssert(!strcmp(username, "default"));
            u = ACLGetUserByName("default",7);
            ACLSetUser(u,"reset",-1);
        }

        /* 加载为此用户定义的每一条规则。 */
        for (int j = 1; aclrules[j]; j++) {
            if (ACLSetUser(u,aclrules[j],sdslen(aclrules[j])) != C_OK) {
                const char *errmsg = ACLSetUserStringError();
                serverLog(LL_WARNING,"Error loading ACL rule '%s' for "
                                     "the user named '%s': %s",
                          aclrules[j],aclrules[0],errmsg);
                return C_ERR;
            }
        }

        /* 配置中存在禁用的用户可能是错误的，
         * 会发出警告但不会向调用者返回任何错误。 */
        if (u->flags & USER_FLAG_DISABLED) {
            serverLog(LL_NOTICE, "The user '%s' is disabled (there is no "
                                 "'on' modifier in the user description). Make "
                                 "sure this is not a configuration error.",
                      aclrules[0]);
        }
    }
    return C_OK;
}

/* 此函数从指定文件名加载ACL：每一行
 * 都会被验证，应该为空或以redis.conf配置或ACL文件中
 * 指定用户的格式出现，即：
 *
 *  user <username> ... 规则 ...
 *
 * 注意，此函数将以'#'开头的注释视为错误，
 * 因为ACL文件旨在被重写，而注释会在重写后丢失。
 * 但允许空行以避免过于严格。
 *
 * 实现ACL LOAD（使用此函数）的一个重要部分是，
 * 如果ACL文件因某种原因无效，则避免以损坏的规则结束，
 * 因此函数将在加载每个用户之前尝试验证规则。
 * 对于发现的每一行损坏的规则，函数会收集错误消息。
 *
 * 重要提示：如果至少有一个错误，则不会加载任何内容，
 * 并且规则将保持原样。
 *
 * 流程结束时，如果在整个文件中未发现错误，则返回NULL。
 * 否则返回一个SDS字符串，在单行中描述所有发现的问题。 */
sds ACLLoadFromFile(const char *filename) {
    FILE *fp;
    char buf[1024];

    /* 打开ACL文件。 */
    if ((fp = fopen(filename,"r")) == NULL) {
        sds errors = sdscatprintf(sdsempty(),
            "Error loading ACLs, opening file '%s': %s",
            filename, strerror(errno));
        return errors;
    }

    /* 将整个文件作为单个字符串加载到内存中。 */
    sds acls = sdsempty();
    while(fgets(buf,sizeof(buf),fp) != NULL)
        acls = sdscat(acls,buf);
    fclose(fp);

    /* 将文件分割成行并尝试加载每一行。 */
    int totlines;
    sds *lines, errors = sdsempty();
    lines = sdssplitlen(acls,strlen(acls),"\n",1,&totlines);
    sdsfree(acls);

    /* 我们在Users基数树的全新实例中进行所有加载操作，
     * 这样如果加载ACL文件出错，我们可以回滚到旧版本。 */
    rax *old_users = Users;
    Users = raxNew();

    /* 加载文件的每一行。 */
    for (int i = 0; i < totlines; i++) {
        sds *argv;
        int argc;
        int linenum = i+1;

        lines[i] = sdstrim(lines[i]," \t\r\n");

        /* 跳过空行 */
        if (lines[i][0] == '\0') continue;

        /* 分割成参数 */
        argv = sdssplitlen(lines[i],sdslen(lines[i])," ",1,&argc);
        if (argv == NULL) {
            errors = sdscatprintf(errors,
                     "%s:%d: unbalanced quotes in acl line. ",
                     server.acl_filename, linenum);
            continue;
        }

        /* 如果生成的命令向量为空，则跳过此行。 */
        if (argc == 0) {
            sdsfreesplitres(argv,argc);
            continue;
        }

        /* 行应该以"user"关键字开头。 */
        if (strcmp(argv[0],"user") || argc < 2) {
            errors = sdscatprintf(errors,
                     "%s:%d should start with user keyword followed "
                     "by the username. ", server.acl_filename,
                     linenum);
            sdsfreesplitres(argv,argc);
            continue;
        }

        /* 用户名中不允许包含空格。 */
        if (ACLStringHasSpaces(argv[1],sdslen(argv[1]))) {
            errors = sdscatprintf(errors,
                     "'%s:%d: username '%s' contains invalid characters. ",
                     server.acl_filename, linenum, argv[1]);
            sdsfreesplitres(argv,argc);
            continue;
        }

        user *u = ACLCreateUser(argv[1],sdslen(argv[1]));

        /* 如果用户已存在，则我们认为是错误并中止。 */
        if (!u) {
            errors = sdscatprintf(errors,"WARNING: Duplicate user '%s' found on line %d. ", argv[1], linenum);
            sdsfreesplitres(argv,argc);
            continue;
        }

        /* 最后处理选项并验证它们是否可以
         * 干净地应用于用户。如果任何选项应用失败，
         * 其他值也不会应用，因为所有待处理的更改都将被丢弃。 */
        int merged_argc;
        sds *acl_args = ACLMergeSelectorArguments(argv + 2, argc - 2, &merged_argc, NULL);
        if (!acl_args) {
            errors = sdscatprintf(errors,
                    "%s:%d: Unmatched parenthesis in selector definition.",
                    server.acl_filename, linenum);
        }

        int syntax_error = 0;
        for (int j = 0; j < merged_argc; j++) {
            acl_args[j] = sdstrim(acl_args[j],"\t\r\n");
            if (ACLSetUser(u,acl_args[j],sdslen(acl_args[j])) != C_OK) {
                const char *errmsg = ACLSetUserStringError();
                if (errno == ENOENT) {
                    /* 对于缺失的命令，我们输出更多信息，因为
                 * 它不应该包含任何敏感信息。 */
                    errors = sdscatprintf(errors,
                            "%s:%d: Error in applying operation '%s': %s. ",
                            server.acl_filename, linenum, acl_args[j], errmsg);
                } else if (syntax_error == 0) {
                    /* 对于所有其他错误，只打印出遇到的第一个错误，
                 * 因为它可能会影响未来的操作。 */
                    errors = sdscatprintf(errors,
                            "%s:%d: %s. ",
                            server.acl_filename, linenum, errmsg);
                    syntax_error = 1;
                }
            }
        }

        for (int i = 0; i < merged_argc; i++) sdsfree(acl_args[i]);
        zfree(acl_args);

        /* 只有在到目前为止没有错误的情况下，才将规则应用到新用户集，
         * 否则这是无用的，因为我们无论如何都会丢弃新的用户集。 */
        if (sdslen(errors) != 0) {
            sdsfreesplitres(argv,argc);
            continue;
        }

        sdsfreesplitres(argv,argc);
    }

    sdsfreesplitres(lines,totlines);

    /* 检查是否发现错误并相应处理。 */
    if (sdslen(errors) == 0) {
        /* 默认用户指针在不同的地方被引用：与其替换这些引用，
         * 不如将新的默认用户配置复制到旧用户中更简单。 */
        user *new_default = ACLGetUserByName("default",7);
        if (!new_default) {
            new_default = ACLCreateDefaultUser();
        }

        ACLCopyUser(DefaultUser,new_default);
        ACLFreeUser(new_default);
        raxInsert(Users,(unsigned char*)"default",7,DefaultUser,NULL);
        raxRemove(old_users,(unsigned char*)"default",7,NULL);

        /* 如果有一些订阅者，我们需要检查是否需要断开一些客户端。 */
        rax *user_channels = NULL;
        if (pubsubTotalSubscriptions() > 0) {
            user_channels = raxNew();
        }

        listIter li;
        listNode *ln;

        listRewind(server.clients,&li);
        while ((ln = listNext(&li)) != NULL) {
            client *c = listNodeValue(ln);
            /* MASTER客户端可以执行所有操作（并且user = NULL），所以我们可以跳过它 */
            if (c->flags & CLIENT_MASTER)
                continue;
            user *original = c->user;
            list *channels = NULL;
            user *new = ACLGetUserByName(c->user->name, sdslen(c->user->name));
            if (new && user_channels) {
                if (!raxFind(user_channels, (unsigned char*)(new->name), sdslen(new->name), (void**)&channels)) {
                    channels = getUpcomingChannelList(new, original);
                    raxInsert(user_channels, (unsigned char*)(new->name), sdslen(new->name), channels, NULL);
                }
            }
            /* 当新通道列表为NULL时，表示新用户的通道列表是旧用户列表的超集。 */
            if (!new || (channels && ACLShouldKillPubsubClient(c, channels))) {
                deauthenticateAndCloseClient(c);
                continue;
            }
            c->user = new;
        }

        if (user_channels)
            raxFreeWithCallback(user_channels, listReleaseGeneric);
        raxFreeWithCallback(old_users, ACLFreeUserGeneric);
        sdsfree(errors);
        return NULL;
    } else {
        raxFreeWithCallback(Users, ACLFreeUserGeneric);
        Users = old_users;
        errors = sdscat(errors,"WARNING: ACL errors detected, no change to the previously active ACL rules was performed");
        return errors;
    }
}

/* 在指定文件名中生成当前内存中ACL的副本。
 * 成功返回C_OK，如果I/O过程中出错则返回C_ERR。
 * 返回C_ERR时会生成日志，提示有关问题的信息。 */
int ACLSaveToFile(const char *filename) {
    sds acl = sdsempty();
    int fd = -1;
    sds tmpfilename = NULL;
    int retval = C_ERR;

    /* 让我们生成一个包含ACL文件新版本的SDS字符串。 */
    raxIterator ri;
    raxStart(&ri,Users);
    raxSeek(&ri,"^",NULL,0);
    while(raxNext(&ri)) {
        user *u = ri.data;
        /* 以配置文件格式返回信息。 */
        sds user = sdsnew("user ");
        user = sdscatsds(user,u->name);
        user = sdscatlen(user," ",1);
        robj *descr = ACLDescribeUser(u);
        user = sdscatsds(user,descr->ptr);
        decrRefCount(descr);
        acl = sdscatsds(acl,user);
        acl = sdscatlen(acl,"\n",1);
        sdsfree(user);
    }
    raxStop(&ri);

    /* 创建一个包含新内容的临时文件。 */
    tmpfilename = sdsnew(filename);
    tmpfilename = sdscatfmt(tmpfilename,".tmp-%i-%I",
        (int) getpid(),commandTimeSnapshot());
    if ((fd = open(tmpfilename,O_WRONLY|O_CREAT,0644)) == -1) {
        serverLog(LL_WARNING,"Opening temp ACL file for ACL SAVE: %s",
            strerror(errno));
        goto cleanup;
    }

    /* 写入文件。 */
    size_t offset = 0;
    while (offset < sdslen(acl)) {
        ssize_t written_bytes = write(fd,acl + offset,sdslen(acl) - offset);
        if (written_bytes <= 0) {
            if (errno == EINTR) continue;
            serverLog(LL_WARNING,"Writing ACL file for ACL SAVE: %s",
                strerror(errno));
            goto cleanup;
        }
        offset += written_bytes;
    }
    if (redis_fsync(fd) == -1) {
        serverLog(LL_WARNING,"Syncing ACL file for ACL SAVE: %s",
            strerror(errno));
        goto cleanup;
    }
    close(fd); fd = -1;

    /* 让我们用新文件替换旧文件。 */
    if (rename(tmpfilename,filename) == -1) {
        serverLog(LL_WARNING,"Renaming ACL file for ACL SAVE: %s",
            strerror(errno));
        goto cleanup;
    }
    if (fsyncFileDir(filename) == -1) {
        serverLog(LL_WARNING,"Syncing ACL directory for ACL SAVE: %s",
            strerror(errno));
        goto cleanup;
    }
    sdsfree(tmpfilename); tmpfilename = NULL;
    retval = C_OK; /* If we reached this point, everything is fine. */

cleanup:
    if (fd != -1) close(fd);
    if (tmpfilename) unlink(tmpfilename);
    sdsfree(tmpfilename);
    sdsfree(acl);
    return retval;
}

/* 此函数在服务器已运行、模块已加载且准备启动时调用，用于从
 * redis.conf中定义的待处理用户列表或ACL文件中加载ACL。
 * 如果用户尝试混合使用两种加载方法，该函数将直接退出并报错。 */
void ACLLoadUsersAtStartup(void) {
    if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) {
        serverLog(LL_WARNING,
            "Configuring Redis with users defined in redis.conf and at "
            "the same setting an ACL file path is invalid. This setup "
            "is very likely to lead to configuration errors and security "
            "holes, please define either an ACL file or declare users "
            "directly in your redis.conf, but not both.");
        exit(1);
    }

    if (ACLLoadConfiguredUsers() == C_ERR) {
        serverLog(LL_WARNING,
            "Critical error while loading ACLs. Exiting.");
        exit(1);
    }

    if (server.acl_filename[0] != '\0') {
        sds errors = ACLLoadFromFile(server.acl_filename);
        if (errors) {
            serverLog(LL_WARNING,
                "Aborting Redis startup because of ACL errors: %s", errors);
            sdsfree(errors);
            exit(1);
        }
    }
}

/* =============================================================================
 * ACL日志
 * ==========================================================================*/

#define ACL_LOG_GROUPING_MAX_TIME_DELTA 60000

/* 此结构定义ACL日志中的一个条目。 */
typedef struct ACLLogEntry {
    uint64_t count;     /* 最近发生此事件的次数。 */
    int reason;         /* 拒绝命令的原因。ACL_DENIED_*。 */
    int context;        /* 顶级、Lua还是MULTI/EXEC环境？ACL_LOG_CTX_*。 */
    sds object;         /* 键名或命令名。 */
    sds username;       /* 客户端认证的用户名。 */
    mstime_t ctime;     /* 此条目的最后更新时间（毫秒）。 */
    sds cinfo;          /* 客户端信息（如有更新则为最后一个客户端）。 */
    long long entry_id;         /* 对（entry_id, timestamp_created）是此条目的唯一标识符，
                                  * 用于节点崩溃并重启后，能够检测这是否是一个新系列。 */
    mstime_t timestamp_created; /* 此条目创建时的UNIX时间（毫秒）。 */
} ACLLogEntry;

/* 此函数将检查ACL条目'a'和'b'是否足够相似，
 * 以至于我们应该更新ACL日志中的现有条目，而不是创建新条目。 */
int ACLLogMatchEntry(ACLLogEntry *a, ACLLogEntry *b) {
    if (a->reason != b->reason) return 0;
    if (a->context != b->context) return 0;
    mstime_t delta = a->ctime - b->ctime;
    if (delta < 0) delta = -delta;
    if (delta > ACL_LOG_GROUPING_MAX_TIME_DELTA) return 0;
    if (sdscmp(a->object,b->object) != 0) return 0;
    if (sdscmp(a->username,b->username) != 0) return 0;
    return 1;
}

/* 释放ACL日志条目。 */
void ACLFreeLogEntry(void *leptr) {
    ACLLogEntry *le = leptr;
    sdsfree(le->object);
    sdsfree(le->username);
    sdsfree(le->cinfo);
    zfree(le);
}

/* 根据原因更新相关计数器 */
void ACLUpdateInfoMetrics(int reason){
    if (reason == ACL_DENIED_AUTH) {
        server.acl_info.user_auth_failures++;
    } else if (reason == ACL_DENIED_CMD) {
        server.acl_info.invalid_cmd_accesses++;
    } else if (reason == ACL_DENIED_KEY) {
        server.acl_info.invalid_key_accesses++;
    } else if (reason == ACL_DENIED_CHANNEL) {
        server.acl_info.invalid_channel_accesses++;
    } else {
        serverPanic("Unknown ACL_DENIED encoding");
    }
}

static void trimACLLogEntriesToMaxLen(void) {
    while(listLength(ACLLog) > server.acllog_max_len) {
        listNode *ln = listLast(ACLLog);
        ACLLogEntry *le = listNodeValue(ln);
        ACLFreeLogEntry(le);
        listDelNode(ACLLog,ln);
    }
}

/* 在ACL日志中添加新条目，确保在达到日志允许的最大长度时删除旧条目。
 * 此函数尝试在当前日志中查找相似条目，以便增加日志条目的计数器，
 * 而不是为非常相似的ACL规则问题创建多个条目。
 *
 * 当原因是ACL_DENIED_KEY或ACL_DENIED_CHANNEL时使用argpos参数，
 * 因为它允许函数记录导致问题的键或通道名称。
 *
 * 最后两个参数是手动覆盖，用于替代依赖于客户端和原因参数的自动设置（默认使用NULL）。
 *
 * 如果`object`不为NULL，此函数将接管它。
 */
void addACLLogEntry(client *c, int reason, int context, int argpos, sds username, sds object) {
    /* 更新ACL信息指标 */
    ACLUpdateInfoMetrics(reason);
    
    if (server.acllog_max_len == 0) {
        trimACLLogEntriesToMaxLen();
        return;
    }
    
    /* 创建一个新条目。 */
    struct ACLLogEntry *le = zmalloc(sizeof(*le));
    le->count = 1;
    le->reason = reason;
    le->username = sdsdup(username ? username : c->user->name);
    le->ctime = commandTimeSnapshot();
    le->entry_id = ACLLogEntryCount;
    le->timestamp_created = le->ctime;

    if (object) {
        le->object = object;
    } else {
        switch(reason) {
            case ACL_DENIED_CMD: le->object = sdsdup(c->cmd->fullname); break;
            case ACL_DENIED_KEY: le->object = sdsdup(c->argv[argpos]->ptr); break;
            case ACL_DENIED_CHANNEL: le->object = sdsdup(c->argv[argpos]->ptr); break;
            case ACL_DENIED_AUTH: le->object = sdsdup(c->argv[0]->ptr); break;
            default: le->object = sdsempty();
        }
    }

    /* 如果我们有来自网络的真实客户端，使用它（在模块计时器上可能不存在） */
    client *realclient = server.current_client? server.current_client : c;

    le->cinfo = catClientInfoString(sdsempty(),realclient);
    le->context = context;

    /* 尝试将此条目与过去的条目匹配，看看我们是否可以
     * 更新现有条目而不是创建新条目。 */
    long toscan = 10; /* 有限地尝试查找重复条目。 */
    listIter li;
    listNode *ln;
    listRewind(ACLLog,&li);
    ACLLogEntry *match = NULL;
    while (toscan-- && (ln = listNext(&li)) != NULL) {
        ACLLogEntry *current = listNodeValue(ln);
        if (ACLLogMatchEntry(current,le)) {
            match = current;
            listDelNode(ACLLog,ln);
            listAddNodeHead(ACLLog,current);
            break;
        }
    }

    /* 如果有匹配项，更新条目，否则将其作为新条目添加。 */
    if (match) {
        /* 我们更新现有条目的几个字段并增加
         * 此条目的事件计数器。 */
        sdsfree(match->cinfo);
        match->cinfo = le->cinfo;
        match->ctime = le->ctime;
        match->count++;

        /* 释放旧条目。 */
        le->cinfo = NULL;
        ACLFreeLogEntry(le);
    } else {
        /* 将其添加到我们的条目列表中。我们需要将列表
         * 修剪到最大大小。 */
        ACLLogEntryCount++; /* Incrementing the entry_id count to make each record in the log unique. */
        listAddNodeHead(ACLLog, le);
        trimACLLogEntriesToMaxLen();
    }
}

sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose) {
    switch (acl_res) {
    case ACL_DENIED_CMD:
        return sdscatfmt(sdsempty(), "User %S has no permissions to run "
                                     "the '%S' command", user->name, cmd->fullname);
    case ACL_DENIED_KEY:
        if (verbose) {
            return sdscatfmt(sdsempty(), "User %S has no permissions to access "
                                         "the '%S' key", user->name, errored_val);
        } else {
            return sdsnew("No permissions to access a key");
        }
    case ACL_DENIED_CHANNEL:
        if (verbose) {
            return sdscatfmt(sdsempty(), "User %S has no permissions to access "
                                         "the '%S' channel", user->name, errored_val);
        } else {
            return sdsnew("No permissions to access a channel");
        }
    }
    serverPanic("Reached deadcode on getAclErrorMessage");
}

/* =============================================================================
 * ACL相关命令
 * ==========================================================================*/

/* ACL CAT 类别 */
void aclCatWithFlags(client *c, dict *commands, uint64_t cflag, int *arraylen) {
    dictEntry *de;
    dictIterator *di = dictGetIterator(commands);

    while ((de = dictNext(di)) != NULL) {
        struct redisCommand *cmd = dictGetVal(de);
        if (cmd->acl_categories & cflag) {
            addReplyBulkCBuffer(c, cmd->fullname, sdslen(cmd->fullname));
            (*arraylen)++;
        }

        if (cmd->subcommands_dict) {
            aclCatWithFlags(c, cmd->subcommands_dict, cflag, arraylen);
        }
    }
    dictReleaseIterator(di);
}

/* 将单个选择器的格式化响应添加到ACL GETUSER响应中。
 * 此函数返回添加的字段数量。
 * 
 * 将verbose设置为1意味着显示键和通道权限的完整限定符。
 */
int aclAddReplySelectorDescription(client *c, aclSelector *s) {
    listIter li;
    listNode *ln;

    /* 命令 */
    addReplyBulkCString(c,"commands");
    sds cmddescr = ACLDescribeSelectorCommandRules(s);
    addReplyBulkSds(c,cmddescr);
    
    /* 键模式 */
    addReplyBulkCString(c,"keys");
    if (s->flags & SELECTOR_FLAG_ALLKEYS) {
        addReplyBulkCBuffer(c,"~*",2);
    } else {
        sds dsl = sdsempty();
        listRewind(s->patterns,&li);
        while((ln = listNext(&li))) {
            keyPattern *thispat = (keyPattern *) listNodeValue(ln);
            if (ln != listFirst(s->patterns)) dsl = sdscat(dsl, " ");
            dsl = sdsCatPatternString(dsl, thispat);
        }
        addReplyBulkSds(c, dsl);
    }

    /* 发布/订阅模式 */
    addReplyBulkCString(c,"channels");
    if (s->flags & SELECTOR_FLAG_ALLCHANNELS) {
        addReplyBulkCBuffer(c,"&*",2);
    } else {
        sds dsl = sdsempty();
        listRewind(s->channels,&li);
        while((ln = listNext(&li))) {
            sds thispat = listNodeValue(ln);
            if (ln != listFirst(s->channels)) dsl = sdscat(dsl, " ");
            dsl = sdscatfmt(dsl, "&%S", thispat);
        }
        addReplyBulkSds(c, dsl);
    }
    return 3;
}

/* ACL -- 显示和修改ACL用户配置。
 * ACL HELP
 * ACL LOAD
 * ACL SAVE
 * ACL LIST
 * ACL USERS
 * ACL CAT [<category>]
 * ACL SETUSER <username> ... acl规则 ...
 * ACL DELUSER <username> [...]
 * ACL GETUSER <username>
 * ACL GENPASS [<bits>]
 * ACL WHOAMI
 * ACL LOG [<count> | RESET]
 */
void aclCommand(client *c) {
    char *sub = c->argv[1]->ptr;
    if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
        /* 最初隐藏所有参数，以避免泄露任何关于用户的信息。 */
        for (int j = 2; j < c->argc; j++) {
            redactClientCommandArgument(c, j);
        }

        sds username = c->argv[2]->ptr;
        /* 检查用户名有效性。 */
        if (ACLStringHasSpaces(username,sdslen(username))) {
            addReplyError(c, "Usernames can't contain spaces or null characters");
            return;
        }

        user *u = ACLGetUserByName(username,sdslen(username));

        sds *temp_argv = zmalloc(c->argc * sizeof(sds));
        for (int i = 3; i < c->argc; i++) temp_argv[i-3] = c->argv[i]->ptr;

        sds error = ACLStringSetUser(u, username, temp_argv, c->argc - 3);
        zfree(temp_argv);
        if (error == NULL) {
            addReply(c,shared.ok);
        } else {
            addReplyErrorSdsSafe(c, error);
        }
        return;
    } else if (!strcasecmp(sub,"deluser") && c->argc >= 3) {
        /* 最初隐藏所有参数，以避免泄露任何关于用户的信息。 */
        for (int j = 2; j < c->argc; j++) redactClientCommandArgument(c, j);

        int deleted = 0;
        for (int j = 2; j < c->argc; j++) {
            sds username = c->argv[j]->ptr;
            if (!strcmp(username,"default")) {
                addReplyError(c,"The 'default' user cannot be removed");
                return;
            }
        }

        for (int j = 2; j < c->argc; j++) {
            sds username = c->argv[j]->ptr;
            user *u;
            if (raxRemove(Users,(unsigned char*)username,
                          sdslen(username),
                          (void**)&u))
            {
                ACLFreeUserAndKillClients(u);
                deleted++;
            }
        }
        addReplyLongLong(c,deleted);
    } else if (!strcasecmp(sub,"getuser") && c->argc == 3) {
        /* 隐藏用户名，以避免泄露任何关于用户的信息。 */
        redactClientCommandArgument(c, 2);

        user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));
        if (u == NULL) {
            addReplyNull(c);
            return;
        }

        void *ufields = addReplyDeferredLen(c);
        int fields = 3;

        /* 标志 */
        addReplyBulkCString(c,"flags");
        void *deflen = addReplyDeferredLen(c);
        int numflags = 0;
        for (int j = 0; ACLUserFlags[j].flag; j++) {
            if (u->flags & ACLUserFlags[j].flag) {
                addReplyBulkCString(c,ACLUserFlags[j].name);
                numflags++;
            }
        }
        setDeferredSetLen(c,deflen,numflags);

        /* 密码 */
        addReplyBulkCString(c,"passwords");
        addReplyArrayLen(c,listLength(u->passwords));
        listIter li;
        listNode *ln;
        listRewind(u->passwords,&li);
        while((ln = listNext(&li))) {
            sds thispass = listNodeValue(ln);
            addReplyBulkCBuffer(c,thispass,sdslen(thispass));
        }
        /* 为了向后兼容，在顶层包含根选择器 */
        fields += aclAddReplySelectorDescription(c, ACLUserGetRootSelector(u));

        /* 描述此用户的所有选择器，包括根选择器的副本 */
        addReplyBulkCString(c,"selectors");
        addReplyArrayLen(c, listLength(u->selectors) - 1);
        listRewind(u->selectors,&li);
        serverAssert(listNext(&li));
        while((ln = listNext(&li))) {
            void *slen = addReplyDeferredLen(c);
            int sfields = aclAddReplySelectorDescription(c, (aclSelector *)listNodeValue(ln));
            setDeferredMapLen(c, slen, sfields);
        } 
        setDeferredMapLen(c, ufields, fields);
    } else if ((!strcasecmp(sub,"list") || !strcasecmp(sub,"users")) &&
               c->argc == 2)
    {
        int justnames = !strcasecmp(sub,"users");
        addReplyArrayLen(c,raxSize(Users));
        raxIterator ri;
        raxStart(&ri,Users);
        raxSeek(&ri,"^",NULL,0);
        while(raxNext(&ri)) {
            user *u = ri.data;
            if (justnames) {
                addReplyBulkCBuffer(c,u->name,sdslen(u->name));
            } else {
                /* 以配置文件格式返回信息。 */
                sds config = sdsnew("user ");
                config = sdscatsds(config,u->name);
                config = sdscatlen(config," ",1);
                robj *descr = ACLDescribeUser(u);
                config = sdscatsds(config,descr->ptr);
                decrRefCount(descr);
                addReplyBulkSds(c,config);
            }
        }
        raxStop(&ri);
    } else if (!strcasecmp(sub,"whoami") && c->argc == 2) {
        if (c->user != NULL) {
            addReplyBulkCBuffer(c,c->user->name,sdslen(c->user->name));
        } else {
            addReplyNull(c);
        }
    } else if (server.acl_filename[0] == '\0' &&
               (!strcasecmp(sub,"load") || !strcasecmp(sub,"save")))
    {
        addReplyError(c,"This Redis instance is not configured to use an ACL file. You may want to specify users via the ACL SETUSER command and then issue a CONFIG REWRITE (assuming you have a Redis configuration file set) in order to store users in the Redis configuration.");
        return;
    } else if (!strcasecmp(sub,"load") && c->argc == 2) {
        sds errors = ACLLoadFromFile(server.acl_filename);
        if (errors == NULL) {
            addReply(c,shared.ok);
        } else {
            addReplyError(c,errors);
            sdsfree(errors);
        }
    } else if (!strcasecmp(sub,"save") && c->argc == 2) {
        if (ACLSaveToFile(server.acl_filename) == C_OK) {
            addReply(c,shared.ok);
        } else {
            addReplyError(c,"There was an error trying to save the ACLs. "
                            "Please check the server logs for more "
                            "information");
        }
    } else if (!strcasecmp(sub,"cat") && c->argc == 2) {
        void *dl = addReplyDeferredLen(c);
        int j;
        for (j = 0; ACLCommandCategories[j].flag != 0; j++)
            addReplyBulkCString(c,ACLCommandCategories[j].name);
        setDeferredArrayLen(c,dl,j);
    } else if (!strcasecmp(sub,"cat") && c->argc == 3) {
        uint64_t cflag = ACLGetCommandCategoryFlagByName(c->argv[2]->ptr);
        if (cflag == 0) {
            addReplyErrorFormat(c, "Unknown category '%.128s'", (char*)c->argv[2]->ptr);
            return;
        }
        int arraylen = 0;
        void *dl = addReplyDeferredLen(c);
        aclCatWithFlags(c, server.orig_commands, cflag, &arraylen);
        setDeferredArrayLen(c,dl,arraylen);
    } else if (!strcasecmp(sub,"genpass") && (c->argc == 2 || c->argc == 3)) {
        #define GENPASS_MAX_BITS 4096
        char pass[GENPASS_MAX_BITS/8*2]; /* Hex representation. */
        long bits = 256; /* By default generate 256 bits passwords. */

        if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&bits,NULL)
            != C_OK) return;

        if (bits <= 0 || bits > GENPASS_MAX_BITS) {
            addReplyErrorFormat(c,
                "ACL GENPASS argument must be the number of "
                "bits for the output password, a positive number "
                "up to %d",GENPASS_MAX_BITS);
            return;
        }

        long chars = (bits+3)/4; /* Round to number of characters to emit. */
        getRandomHexChars(pass,chars);
        addReplyBulkCBuffer(c,pass,chars);
    } else if (!strcasecmp(sub,"log") && (c->argc == 2 || c->argc ==3)) {
        long count = 10; /* Number of entries to emit by default. */

        /* 解析LOG可能的唯一参数：它可以是用户想要显示的条目数量，
         * 或者是"RESET"命令以刷新旧条目。 */
        if (c->argc == 3) {
            if (!strcasecmp(c->argv[2]->ptr,"reset")) {
                listSetFreeMethod(ACLLog,ACLFreeLogEntry);
                listEmpty(ACLLog);
                listSetFreeMethod(ACLLog,NULL);
                addReply(c,shared.ok);
                return;
            } else if (getLongFromObjectOrReply(c,c->argv[2],&count,NULL)
                       != C_OK)
            {
                return;
            }
            if (count < 0) count = 0;
        }

        /* 根据我们拥有的条目数量调整计数。 */
        if ((size_t)count > listLength(ACLLog))
            count = listLength(ACLLog);

        addReplyArrayLen(c,count);
        listIter li;
        listNode *ln;
        listRewind(ACLLog,&li);
        mstime_t now = commandTimeSnapshot();
        while (count-- && (ln = listNext(&li)) != NULL) {
            ACLLogEntry *le = listNodeValue(ln);
            addReplyMapLen(c,10);
            addReplyBulkCString(c,"count");
            addReplyLongLong(c,le->count);

            addReplyBulkCString(c,"reason");
            char *reasonstr;
            switch(le->reason) {
            case ACL_DENIED_CMD: reasonstr="command"; break;
            case ACL_DENIED_KEY: reasonstr="key"; break;
            case ACL_DENIED_CHANNEL: reasonstr="channel"; break;
            case ACL_DENIED_AUTH: reasonstr="auth"; break;
            default: reasonstr="unknown";
            }
            addReplyBulkCString(c,reasonstr);

            addReplyBulkCString(c,"context");
            char *ctxstr;
            switch(le->context) {
            case ACL_LOG_CTX_TOPLEVEL: ctxstr="toplevel"; break;
            case ACL_LOG_CTX_MULTI: ctxstr="multi"; break;
            case ACL_LOG_CTX_LUA: ctxstr="lua"; break;
            case ACL_LOG_CTX_MODULE: ctxstr="module"; break;
            default: ctxstr="unknown";
            }
            addReplyBulkCString(c,ctxstr);

            addReplyBulkCString(c,"object");
            addReplyBulkCBuffer(c,le->object,sdslen(le->object));
            addReplyBulkCString(c,"username");
            addReplyBulkCBuffer(c,le->username,sdslen(le->username));
            addReplyBulkCString(c,"age-seconds");
            double age = (double)(now - le->ctime)/1000;
            addReplyDouble(c,age);
            addReplyBulkCString(c,"client-info");
            addReplyBulkCBuffer(c,le->cinfo,sdslen(le->cinfo));
            addReplyBulkCString(c, "entry-id");
            addReplyLongLong(c, le->entry_id);
            addReplyBulkCString(c, "timestamp-created");
            addReplyLongLong(c, le->timestamp_created);
            addReplyBulkCString(c, "timestamp-last-updated");
            addReplyLongLong(c, le->ctime);
        }
    } else if (!strcasecmp(sub,"dryrun") && c->argc >= 4) {
        struct redisCommand *cmd;
        user *u = ACLGetUserByName(c->argv[2]->ptr,sdslen(c->argv[2]->ptr));
        if (u == NULL) {
            addReplyErrorFormat(c, "User '%s' not found", (char *)c->argv[2]->ptr);
            return;
        }

        if ((cmd = lookupCommand(c->argv + 3, c->argc - 3)) == NULL) {
            addReplyErrorFormat(c, "Command '%s' not found", (char *)c->argv[3]->ptr);
            return;
        }

        if ((cmd->arity > 0 && cmd->arity != c->argc-3) ||
            (c->argc-3 < -cmd->arity))
        {
            addReplyErrorFormat(c,"wrong number of arguments for '%s' command", cmd->fullname);
            return;
        }

        int idx;
        int result = ACLCheckAllUserCommandPerm(u, cmd, c->argv + 3, c->argc - 3, &idx);
        if (result != ACL_OK) {
            sds err = getAclErrorMessage(result, u, cmd,  c->argv[idx+3]->ptr, 1);
            addReplyBulkSds(c, err);
            return;
        }

        addReply(c,shared.ok);
    } else if (c->argc == 2 && !strcasecmp(sub,"help")) {
        const char *help[] = {
"CAT [<category>]",
"    List all commands that belong to <category>, or all command categories",
"    when no category is specified.",
"DELUSER <username> [<username> ...]",
"    Delete a list of users.",
"DRYRUN <username> <command> [<arg> ...]",
"    Returns whether the user can execute the given command without executing the command.",
"GETUSER <username>",
"    Get the user's details.",
"GENPASS [<bits>]",
"    Generate a secure 256-bit user password. The optional `bits` argument can",
"    be used to specify a different size.",
"LIST",
"    Show users details in config file format.",
"LOAD",
"    Reload users from the ACL file.",
"LOG [<count> | RESET]",
"    Show the ACL log entries.",
"SAVE",
"    Save the current config to the ACL file.",
"SETUSER <username> <attribute> [<attribute> ...]",
"    Create or modify a user with the specified attributes.",
"USERS",
"    List all the registered usernames.",
"WHOAMI",
"    Return the current connection username.",
NULL
        };
        addReplyHelp(c,help);
    } else {
        addReplySubcommandSyntaxError(c);
    }
}

void addReplyCommandCategories(client *c, struct redisCommand *cmd) {
    int flagcount = 0;
    void *flaglen = addReplyDeferredLen(c);
    for (int j = 0; ACLCommandCategories[j].flag != 0; j++) {
        if (cmd->acl_categories & ACLCommandCategories[j].flag) {
            addReplyStatusFormat(c, "@%s", ACLCommandCategories[j].name);
            flagcount++;
        }
    }
    setDeferredSetLen(c, flaglen, flagcount);
}

/* 成功时，启动一个内部连接，能够执行
 * 内部命令（参见CMD_INTERNAL）。 */
static void internalAuth(client *c) {
    if (server.cluster == NULL) {
        addReplyError(c, "Cannot authenticate as an internal connection on non-cluster instances");
        return;
    }

    sds password = c->argv[2]->ptr;

    /* 获取内部密钥。 */
    size_t len = -1;
    const char *internal_secret = clusterGetSecret(&len);
    if (sdslen(password) != len) {
        addReplyError(c, "-WRONGPASS invalid internal password");
        return;
    }
    if (!time_independent_strcmp((char *)internal_secret, (char *)password, len)) {
        c->flags |= CLIENT_INTERNAL;
        /* No further authentication is needed. */
        c->authenticated = 1;
        /* Set the user to the unrestricted user, if it is not already set (default). */
        if (c->user != NULL) {
            c->user = NULL;
            moduleNotifyUserChanged(c);
        }
        addReply(c, shared.ok);
    } else {
        addReplyError(c, "-WRONGPASS invalid internal password");
    }
}

/* AUTH <password>
 * AUTH <username> <password>（Redis >= 6.0形式）
 *
 * 当省略用户时，表示我们正在尝试通过
 * 默认用户进行身份验证。 */
void authCommand(client *c) {
    /* Only two or three argument forms are allowed. */
    if (c->argc > 3) {
        addReplyErrorObject(c,shared.syntaxerr);
        return;
    }
    /* 始终隐藏第二个参数 */
    redactClientCommandArgument(c, 1);

    /* 处理两种不同的形式。两个参数的形式
     * 将仅使用"default"作为用户名。 */
    robj *username, *password;
    if (c->argc == 2) {
        /* 模拟旧行为：如果未配置密码，对两个参数形式的请求返回错误。 */
        if (DefaultUser->flags & USER_FLAG_NOPASS) {
            addReplyError(c,"AUTH <password> called without any password "
                            "configured for the default user. Are you sure "
                            "your configuration is correct?");
            return;
        }

        username = shared.default_username; 
        password = c->argv[1];
    } else {
        username = c->argv[1];
        password = c->argv[2];
        redactClientCommandArgument(c, 2);

        /* 处理内部认证命令。
         * 注意：用户定义的ACL用户不能使用此用户名（不允许空格），
         * 因此不可能与ACL发生冲突。 */
        if (!strcmp(username->ptr, "internal connection")) {
            internalAuth(c);
            return;
        }
    }

    robj *err = NULL;
    int result = ACLAuthenticateUser(c, username, password, &err);
    if (result == AUTH_OK) {
        addReply(c, shared.ok);
    } else if (result == AUTH_ERR) {
        addAuthErrReply(c, err);
    }
    if (err) decrRefCount(err);
}

/* 为"default" ACL用户设置密码。这实现了对
 * requirepass配置的支持，因此传入NULL将设置用户为nopass。 */
void ACLUpdateDefaultUserPassword(sds password) {
    ACLSetUser(DefaultUser,"resetpass",-1);
    if (password) {
        sds aclop = sdscatlen(sdsnew(">"), password, sdslen(password));
        ACLSetUser(DefaultUser,aclop,sdslen(aclop));
        sdsfree(aclop);
    } else {
        ACLSetUser(DefaultUser,"nopass",-1);
    }
}
